Basic work to support deep color channels:
[mesa.git] / src / mesa / drivers / glide / fxtexman.c
index 5ee145e3cbf0d9232b3d67d80a3c48639b2a773e..2ffaeadd7e0ef6fb4d07cf92d692eb0744d9512f 100644 (file)
@@ -1,27 +1,51 @@
-/* -*- mode: C; tab-width:8;  -*-
-
-             fxtexman.c - 3Dfx VooDoo texture memory functions
-*/
+/* -*- mode: C; tab-width:8; c-basic-offset:2 -*- */
 
 /*
- * 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:  3.3
+ *
+ * Copyright (C) 1999-2000  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
  *
- * 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.
+ * Original Mesa / 3Dfx device driver (C) 1999 David Bucciarelli, by the
+ * terms stated above.
  *
- * 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.
+ * Thank you for your contribution, David!
  *
- * See the file fxapi.c for more informations about authors
+ * Please make note of the above copyright/license statement.  If you
+ * contributed code or bug fixes to this code under the previous (GNU
+ * Library) license and object to the new license, your code will be
+ * removed at your request.  Please see the Mesa docs/COPYRIGHT file
+ * for more information.
  *
+ * Additional Mesa/3Dfx driver developers:
+ *   Daryll Strauss <daryll@precisioninsight.com>
+ *   Keith Whitwell <keith@precisioninsight.com>
+ *
+ * See fxapi.h for more revision/author details.
  */
 
+
+/* fxtexman.c - 3Dfx VooDoo texture memory functions */
+
+
 #ifdef HAVE_CONFIG_H
 #include "conf.h"
 #endif
 
 #include "fxdrv.h"
 
-static tfxTMFreeNode *fxTMNewTMFreeNode(FxU32 start, FxU32 end)
+int texSwaps=0;
+
+#define FX_2MB_SPLIT 0x200000
+
+static struct gl_texture_object *fxTMFindOldestObject(fxMesaContext fxMesa,
+                                                     int tmu);
+
+
+#ifdef TEXSANITY
+static void fubar()
 {
-  tfxTMFreeNode *tmn;
+}
 
-  if(!(tmn=malloc(sizeof(tfxTMFreeNode)))) {
-    fprintf(stderr,"fx Driver: out of memory !\n");
-    fxCloseHardware();
-    exit(-1);
+  /* Sanity Check */
+static void sanity(fxMesaContext fxMesa)
+{
+  MemRange *tmp, *prev, *pos;
+
+  prev=0;
+  tmp = fxMesa->tmFree[0];
+  while (tmp) {
+    if (!tmp->startAddr && !tmp->endAddr) {
+      fprintf(stderr, "Textures fubar\n");
+      fubar();
+    }
+    if (tmp->startAddr>=tmp->endAddr) {
+      fprintf(stderr, "Node fubar\n");
+      fubar();
+    }
+    if (prev && (prev->startAddr>=tmp->startAddr || 
+       prev->endAddr>tmp->startAddr)) {
+      fprintf(stderr, "Sorting fubar\n");
+      fubar();
+    }
+    prev=tmp;
+    tmp=tmp->next;
   }
+  prev=0;
+  tmp = fxMesa->tmFree[1];
+  while (tmp) {
+    if (!tmp->startAddr && !tmp->endAddr) {
+      fprintf(stderr, "Textures fubar\n");
+      fubar();
+    }
+    if (tmp->startAddr>=tmp->endAddr) {
+      fprintf(stderr, "Node fubar\n");
+      fubar();
+    }
+    if (prev && (prev->startAddr>=tmp->startAddr || 
+       prev->endAddr>tmp->startAddr)) {
+      fprintf(stderr, "Sorting fubar\n");
+      fubar();
+    }
+    prev=tmp;
+    tmp=tmp->next;
+  }
+}
+#endif
 
-  tmn->next=NULL;
-  tmn->startAddress=start;
-  tmn->endAddress=end;
+static MemRange *fxTMNewRangeNode(fxMesaContext fxMesa, FxU32 start, FxU32 end) {
+  MemRange *result=0;
 
-  return tmn;
+  if (fxMesa->tmPool) {
+    result=fxMesa->tmPool;
+    fxMesa->tmPool=fxMesa->tmPool->next;
+  } else {
+    if (!(result=MALLOC(sizeof(MemRange)))) {
+      fprintf(stderr, "fxDriver: out of memory!\n");
+      fxCloseHardware();
+      exit(-1);
+    }
+  }
+  result->startAddr=start;
+  result->endAddr=end;
+  return result;
+}
+
+static void fxTMDeleteRangeNode(fxMesaContext fxMesa, MemRange *range)
+{
+  range->next=fxMesa->tmPool;
+  fxMesa->tmPool=range;
 }
 
 static void fxTMUInit(fxMesaContext fxMesa, int tmu)
 {
-  tfxTMFreeNode *tmn,*tmntmp;
+  MemRange *tmn, *last;
   FxU32 start,end,blockstart,blockend;
 
-  start=grTexMinAddress(tmu);
-  end=grTexMaxAddress(tmu);
+  start=FX_grTexMinAddress(tmu);
+  end=FX_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");
+    fprintf(stderr,"Voodoo %s configuration:",(tmu==FX_TMU0) ? "TMU0" : "TMU1");
+    fprintf(stderr,"Voodoo  Lower texture memory address (%u)\n",(unsigned int)start);
+    fprintf(stderr,"Voodoo  Higher texture memory address (%u)\n",(unsigned int)end);
+    fprintf(stderr,"Voodoo  Splitting Texture memory in 2b blocks:\n");
   }
 
   fxMesa->freeTexMem[tmu]=end-start;
   fxMesa->tmFree[tmu]=NULL;
-  fxMesa->tmAlloc[tmu]=NULL;
 
+  last=0;
   blockstart=start;
-  while(blockstart<=end) {
-    if(blockstart+0x1fffff>end)
-      blockend=end;
-    else
-      blockend=blockstart+0x1fffff;
+  while (blockstart<end) {
+    if (blockstart+FX_2MB_SPLIT>end) blockend=end;
+    else blockend=blockstart+FX_2MB_SPLIT;
 
     if(fxMesa->verbose)
-      fprintf(stderr,"    %07u-%07u\n",(unsigned int)blockstart,(unsigned int)blockend);
+      fprintf(stderr,"Voodoo    %07u-%07u\n",
+             (unsigned int)blockstart,(unsigned int)blockend);
 
-    tmn=fxTMNewTMFreeNode(blockstart,blockend);
+    tmn=fxTMNewRangeNode(fxMesa, blockstart, blockend);
+    tmn->next=0;
 
-    if(fxMesa->tmFree[tmu]) {
-      for(tmntmp=fxMesa->tmFree[tmu];tmntmp->next!=NULL;tmntmp=tmntmp->next){};
-      tmntmp->next=tmn;
-    } else
-      fxMesa->tmFree[tmu]=tmn;
+    if (last) last->next=tmn;
+    else fxMesa->tmFree[tmu]=tmn;
+    last=tmn;
 
-    blockstart+=0x1fffff+1;
+    blockstart+=FX_2MB_SPLIT;
   }
 }
 
-void fxTMInit(fxMesaContext fxMesa)
-{
-  fxTMUInit(fxMesa,FX_TMU0);
-
-  if(fxMesa->haveTwoTMUs)
-    fxTMUInit(fxMesa,FX_TMU1);
-
-  fxMesa->texBindNumber=0;
-}
-
-static struct gl_texture_object *fxTMFindOldestTMBlock(fxMesaContext fxMesa,
-                                                      tfxTMAllocNode *tmalloc,
-                                                      GLuint texbindnumber)
+static int fxTMFindStartAddr(fxMesaContext fxMesa, GLint tmu, int size)
 {
-  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;
+  MemRange *prev, *tmp;
+  int result;
+  struct gl_texture_object *obj;
+
+  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;
     }
-
-    tmalloc=tmalloc->next;
+    /* No free space. Discard oldest */
+    obj=fxTMFindOldestObject(fxMesa, tmu);
+    if (!obj) {
+      fprintf(stderr, "fx Driver: No space for texture\n");
+      return -1;
+    }
+    fxTMMoveOutTM(fxMesa, obj);
+    texSwaps++;
   }
-
-  return oldesttexobj;
-}
-
-static GLboolean fxTMFreeOldTMBlock(fxMesaContext fxMesa, GLint tmu)
-{
-  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;
 }
 
-static tfxTMFreeNode *fxTMExtractTMFreeBlock(tfxTMFreeNode *tmfree, int texmemsize,
-                                            GLboolean *success, FxU32 *startadr)
+static void fxTMRemoveRange(fxMesaContext fxMesa, GLint tmu, MemRange *range)
 {
-  int blocksize;
-
-  /* TO DO: cut recursion */
+  MemRange *tmp, *prev;
 
-  if(!tmfree) {
-    *success=GL_FALSE;
-    return NULL;
+  if (range->startAddr==range->endAddr) {
+    fxTMDeleteRangeNode(fxMesa, range);
+    return;
   }
-
-  blocksize=(int)tmfree->endAddress-(int)tmfree->startAddress+1;
-
-  if(blocksize==texmemsize) {
-    tfxTMFreeNode *nexttmfree;
-
-    *success=GL_TRUE;
-    *startadr=tmfree->startAddress;
-
-    nexttmfree=tmfree->next;
-    free(tmfree);
-
-    return nexttmfree;
+  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;
   }
-
-  if(blocksize>texmemsize) {
-    *success=GL_TRUE;
-    *startadr=tmfree->startAddress;
-
-    tmfree->startAddress+=texmemsize;
-
-    return tmfree;
+  /* 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&(FX_2MB_SPLIT-1)) { 
+      /* Combine */
+      tmp->startAddr=range->startAddr;
+      fxTMDeleteRangeNode(fxMesa, range);
+      range=tmp;
+    }
+  }
+  if (prev) {
+    if (prev->endAddr==range->startAddr && range->startAddr&(FX_2MB_SPLIT-1)) { 
+      /* Combine */
+      prev->endAddr=range->endAddr;
+      prev->next=range->next;
+      fxTMDeleteRangeNode(fxMesa, range);
+    } else prev->next=range;
+  } else {
+    fxMesa->tmFree[tmu]=range;
   }
-
-  tmfree->next=fxTMExtractTMFreeBlock(tmfree->next,texmemsize,success,startadr);
-
-  return tmfree;
 }
 
-static tfxTMAllocNode *fxTMGetTMBlock(fxMesaContext fxMesa, struct gl_texture_object *tObj,
-                                     GLint tmu, int texmemsize)
+static struct gl_texture_object *fxTMFindOldestObject(fxMesaContext fxMesa,
+                                                     int tmu)
 {
-  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;
-
-      fxMesa->freeTexMem[tmu]-=texmemsize;
-
-      if(!(newtmalloc=malloc(sizeof(tfxTMAllocNode)))) {
-       fprintf(stderr,"fx Driver: out of memory !\n");
-       fxCloseHardware();
-       exit(-1);
+  GLuint age, old, lasttime, bindnumber;
+  tfxTexInfo *info;
+  struct gl_texture_object *obj, *tmp;
+
+  tmp=fxMesa->glCtx->Shared->TexObjectList;
+  if (!tmp) return 0;
+  obj=0;
+  old=0;
+
+  bindnumber=fxMesa->texBindNumber;
+  while (tmp) {
+    info=fxTMGetTexInfo(tmp);
+
+    if (info && info->isInTM &&
+       ((info->whichTMU==tmu) || (info->whichTMU==FX_TMU_BOTH) || 
+       (info->whichTMU==FX_TMU_SPLIT))) {
+      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;
       }
-      
-      newtmalloc->next=fxMesa->tmAlloc[tmu];
-      newtmalloc->startAddress=startadr;
-      newtmalloc->endAddress=startadr+texmemsize-1;
-      newtmalloc->tObj=tObj;
-
-      fxMesa->tmAlloc[tmu]=newtmalloc;
-
-      return newtmalloc;
-    }
-
-    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);
     }
+    tmp=tmp->Next;
   }
+  return obj;
+}
+
+static MemRange *fxTMAddObj(fxMesaContext fxMesa, 
+                           struct gl_texture_object *tObj,
+                           GLint tmu, int texmemsize)
+{
+  FxU32 startAddr;
+  MemRange *range;
+
+  startAddr=fxTMFindStartAddr(fxMesa, tmu, texmemsize);
+  if (startAddr<0) return 0;
+  range=fxTMNewRangeNode(fxMesa, startAddr, startAddr+texmemsize);
+  return range;
 }
 
-void fxTMMoveInTM(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint where)
+/* External Functions */
+
+void fxTMMoveInTM_NoLock(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint where)
 {
-  tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
+  tfxTexInfo *ti=fxTMGetTexInfo(tObj);
   int i,l;
   int texmemsize;
 
@@ -236,54 +318,112 @@ void fxTMMoveInTM(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint wh
 
   fxMesa->stats.reqTexUpload++;
 
-  if(!ti->validated) {
+  if (!ti->validated) {
     fprintf(stderr,"fx Driver: internal error in fxTMMoveInTM() -> not validated\n");
     fxCloseHardware();
     exit(-1);
   }
 
-  if(ti->tmi.isInTM)
-    return;
+  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 (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;
+  ti->whichTMU=(FxU32)where;
 
-  switch(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);
+    texmemsize=(int)FX_grTexTextureMemRequired_NoLock(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++)
-      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);
+    for (i=FX_largeLodValue(ti->info), l=ti->minLevel;
+        i<=FX_smallLodValue(ti->info);
+        i++,l++)
+      FX_grTexDownloadMipMapLevel_NoLock(where,
+                                        ti->tm[where]->startAddr,
+                                        FX_valueToLod(i),
+                                        FX_largeLodLog2(ti->info),
+                                        FX_aspectRatioLog2(ti->info),
+                                        ti->info.format,
+                                        GR_MIPMAPLEVELMASK_BOTH,
+                                        ti->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);
+  case FX_TMU_SPLIT: 
+    texmemsize=(int)FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_ODD,
+                                                     &(ti->info));
+    ti->tm[FX_TMU0]=fxTMAddObj(fxMesa, tObj, FX_TMU0, texmemsize);
+    fxMesa->stats.memTexUpload+=texmemsize;
+
+    texmemsize=(int)FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_EVEN,
+                                                     &(ti->info));
+    ti->tm[FX_TMU1]=fxTMAddObj(fxMesa, tObj, FX_TMU1, texmemsize);
     fxMesa->stats.memTexUpload+=texmemsize;
 
-    texmemsize=(int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_EVEN,&(ti->info));
-    ti->tmi.tm[FX_TMU1]=fxTMGetTMBlock(fxMesa,tObj,FX_TMU1,texmemsize);
+    for (i=FX_largeLodValue(ti->info),l=ti->minLevel;
+        i<=FX_smallLodValue(ti->info);
+        i++,l++) {
+      FX_grTexDownloadMipMapLevel_NoLock(GR_TMU0,
+                                        ti->tm[FX_TMU0]->startAddr,
+                                        FX_valueToLod(i),
+                                        FX_largeLodLog2(ti->info),
+                                        FX_aspectRatioLog2(ti->info),
+                                        ti->info.format,
+                                        GR_MIPMAPLEVELMASK_ODD,
+                                        ti->mipmapLevel[l].data);
+
+      FX_grTexDownloadMipMapLevel_NoLock(GR_TMU1,
+                                        ti->tm[FX_TMU1]->startAddr,
+                                        FX_valueToLod(i),
+                                        FX_largeLodLog2(ti->info),
+                                        FX_aspectRatioLog2(ti->info),
+                                        ti->info.format,
+                                        GR_MIPMAPLEVELMASK_EVEN,
+                                        ti->mipmapLevel[l].data);
+    }
+    break;
+  case FX_TMU_BOTH:
+    texmemsize=(int)FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_BOTH,
+                                                     &(ti->info));
+    ti->tm[FX_TMU0]=fxTMAddObj(fxMesa, tObj, FX_TMU0, 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);
+    texmemsize=(int)FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_BOTH,
+                                                     &(ti->info));
+    ti->tm[FX_TMU1]=fxTMAddObj(fxMesa, tObj, FX_TMU1, texmemsize);
+    fxMesa->stats.memTexUpload+=texmemsize;
 
-      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);
+    for (i=FX_largeLodValue(ti->info),l=ti->minLevel;
+        i<=FX_smallLodValue(ti->info);
+        i++,l++) {
+      FX_grTexDownloadMipMapLevel_NoLock(GR_TMU0,
+                                        ti->tm[FX_TMU0]->startAddr,
+                                        FX_valueToLod(i),
+                                        FX_largeLodLog2(ti->info),
+                                        FX_aspectRatioLog2(ti->info),
+                                        ti->info.format,
+                                        GR_MIPMAPLEVELMASK_BOTH,
+                                        ti->mipmapLevel[l].data);
+
+      FX_grTexDownloadMipMapLevel_NoLock(GR_TMU1,
+                                        ti->tm[FX_TMU1]->startAddr,
+                                        FX_valueToLod(i),
+                                        FX_largeLodLog2(ti->info),
+                                        FX_aspectRatioLog2(ti->info),
+                                        ti->info.format,
+                                        GR_MIPMAPLEVELMASK_BOTH,
+                                        ti->mipmapLevel[l].data);
     }
     break;
   default:
@@ -294,49 +434,89 @@ void fxTMMoveInTM(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint wh
 
   fxMesa->stats.texUpload++;
 
-  ti->tmi.isInTM=GL_TRUE;
+  ti->isInTM=GL_TRUE;
+}
+
+void fxTMMoveInTM(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint where) {
+  BEGIN_BOARD_LOCK();
+  fxTMMoveInTM_NoLock(fxMesa, tObj, where);
+  END_BOARD_LOCK();
 }
 
 void fxTMReloadMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint level)
 {
-  tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
+  tfxTexInfo *ti=fxTMGetTexInfo(tObj);
   GrLOD_t lodlevel;
   GLint tmu;
 
-  if(!ti->validated) {
+  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);
+  tmu=(int)ti->whichTMU;
+  fxTMMoveInTM(fxMesa, tObj, tmu);
 
-  fxTexGetInfo(ti->tmi.mipmapLevel[0].width,ti->tmi.mipmapLevel[0].height,
-              &lodlevel,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+  fxTexGetInfo(ti->mipmapLevel[0].width,ti->mipmapLevel[0].height,
+              &lodlevel, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 
+#ifdef FX_GLIDE3
+  lodlevel-=level;
+#else
+  lodlevel+=level;
+#endif
   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);
+    FX_grTexDownloadMipMapLevel(tmu,
+                               ti->tm[tmu]->startAddr,
+                               FX_valueToLod(FX_lodToValue(lodlevel)),
+                               FX_largeLodLog2(ti->info),
+                               FX_aspectRatioLog2(ti->info),
+                               ti->info.format,
+                               GR_MIPMAPLEVELMASK_BOTH,
+                               ti->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);
+  case FX_TMU_SPLIT:
+    FX_grTexDownloadMipMapLevel(GR_TMU0,
+                               ti->tm[GR_TMU0]->startAddr,
+                               FX_valueToLod(FX_lodToValue(lodlevel)),
+                               FX_largeLodLog2(ti->info),
+                               FX_aspectRatioLog2(ti->info),
+                               ti->info.format,
+                               GR_MIPMAPLEVELMASK_ODD,
+                               ti->mipmapLevel[level].data);
+    
+    FX_grTexDownloadMipMapLevel(GR_TMU1,
+                               ti->tm[GR_TMU1]->startAddr,
+                               FX_valueToLod(FX_lodToValue(lodlevel)),
+                               FX_largeLodLog2(ti->info),
+                               FX_aspectRatioLog2(ti->info),
+                               ti->info.format,
+                               GR_MIPMAPLEVELMASK_EVEN,
+                               ti->mipmapLevel[level].data);
+    break;
+  case FX_TMU_BOTH:
+    FX_grTexDownloadMipMapLevel(GR_TMU0,
+                               ti->tm[GR_TMU0]->startAddr,
+                               FX_valueToLod(FX_lodToValue(lodlevel)),
+                               FX_largeLodLog2(ti->info),
+                               FX_aspectRatioLog2(ti->info),
+                               ti->info.format,
+                               GR_MIPMAPLEVELMASK_BOTH,
+                               ti->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);
+    FX_grTexDownloadMipMapLevel(GR_TMU1,
+                               ti->tm[GR_TMU1]->startAddr,
+                               FX_valueToLod(FX_lodToValue(lodlevel)),
+                               FX_largeLodLog2(ti->info),
+                               FX_aspectRatioLog2(ti->info),
+                               ti->info.format,
+                               GR_MIPMAPLEVELMASK_BOTH,
+                               ti->mipmapLevel[level].data);
     break;
+
   default:
     fprintf(stderr,"fx Driver: internal error in fxTMReloadMipMapLevel() -> wrong tmu (%d)\n",tmu);
     fxCloseHardware();
@@ -344,10 +524,11 @@ void fxTMReloadMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj,
   }
 }
 
-void fxTMReloadSubMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj,
+void fxTMReloadSubMipMapLevel(fxMesaContext fxMesa, 
+                             struct gl_texture_object *tObj,
                              GLint level, GLint yoffset, GLint height)
 {
-  tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
+  tfxTexInfo *ti=fxTMGetTexInfo(tObj);
   GrLOD_t lodlevel;
   unsigned short *data;
   GLint tmu;
@@ -358,43 +539,73 @@ void fxTMReloadSubMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tO
     exit(-1);
   }
 
-  tmu=(int)ti->tmi.whichTMU;
-  fxTMMoveInTM(fxMesa,tObj,tmu);
+  tmu=(int)ti->whichTMU;
+  fxTMMoveInTM(fxMesa, tObj, tmu);
 
-  fxTexGetInfo(ti->tmi.mipmapLevel[0].width,ti->tmi.mipmapLevel[0].height,
-              &lodlevel,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+  fxTexGetInfo(ti->mipmapLevel[0].width, ti->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);
+    data=ti->mipmapLevel[level].data+((yoffset*ti->mipmapLevel[level].width)>>1);
   else
-    data=ti->tmi.mipmapLevel[level].data+yoffset*ti->tmi.mipmapLevel[level].width;
+    data=ti->mipmapLevel[level].data+yoffset*ti->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);
+    FX_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: /* 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);
+  case FX_TMU_SPLIT:
+    FX_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);
+
+    FX_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:
+    FX_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);
+
+    FX_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,"fx Driver: internal error in fxTMReloadSubMipMapLevel() -> wrong tmu (%d)\n",tmu);
@@ -403,103 +614,25 @@ void fxTMReloadSubMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tO
   }
 }
 
-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;
-}
-
-static tfxTMFreeNode *fxTMAddTMFree(tfxTMFreeNode *tmfree, FxU32 startadr, FxU32 endadr)
-{
-  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;
-
-      nexttmfree=tmfree->next->next;
-      free(tmfree->next);
-
-      tmfree->next=nexttmfree;
-    }
-
-
-    return tmfree;
-  }
-
-  if(startadr<tmfree->startAddress) {
-    tfxTMFreeNode *newtmfree;
-
-    newtmfree=fxTMNewTMFreeNode(startadr,endadr);
-    newtmfree->next=tmfree;
-
-    return newtmfree;
-  }
-
-  tmfree->next=fxTMAddTMFree(tmfree->next,startadr,endadr);
-
-  return tmfree;
-}
-
-static void fxTMFreeTMBlock(fxMesaContext fxMesa, GLint tmu, tfxTMAllocNode *tmalloc)
-{
-  FxU32 startadr,endadr;
-
-  startadr=tmalloc->startAddress;
-  endadr=tmalloc->endAddress;
-
-  fxMesa->tmAlloc[tmu]=fxTMFreeTMAllocBlock(fxMesa->tmAlloc[tmu],tmalloc);
-
-  fxMesa->tmFree[tmu]=fxTMAddTMFree(fxMesa->tmFree[tmu],startadr,endadr);
-
-  fxMesa->freeTexMem[tmu]+=endadr-startadr+1;
-}
-
 void fxTMMoveOutTM(fxMesaContext fxMesa, struct gl_texture_object *tObj)
 {
-  tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
+  tfxTexInfo *ti=fxTMGetTexInfo(tObj);
 
   if (MESA_VERBOSE&VERBOSE_DRIVER) {
      fprintf(stderr,"fxmesa: fxTMMoveOutTM(%x (%d))\n",(GLuint)tObj,tObj->Name);
   }
 
-  if(!ti->tmi.isInTM)
-    return;
+  if (!ti->isInTM)  return;
 
-  switch(ti->tmi.whichTMU) {
+  switch(ti->whichTMU) {
   case FX_TMU0:
   case FX_TMU1:
-    fxTMFreeTMBlock(fxMesa,(int)ti->tmi.whichTMU,ti->tmi.tm[ti->tmi.whichTMU]);
+    fxTMRemoveRange(fxMesa, (int)ti->whichTMU, ti->tm[ti->whichTMU]);
     break;
   case FX_TMU_SPLIT:
-    fxTMFreeTMBlock(fxMesa,FX_TMU0,ti->tmi.tm[FX_TMU0]);
-    fxTMFreeTMBlock(fxMesa,FX_TMU1,ti->tmi.tm[FX_TMU1]);
+  case FX_TMU_BOTH:
+    fxTMRemoveRange(fxMesa, FX_TMU0, ti->tm[FX_TMU0]);
+    fxTMRemoveRange(fxMesa, FX_TMU1, ti->tm[FX_TMU1]);
     break;
   default:
     fprintf(stderr,"fx Driver: internal error in fxTMMoveOutTM()\n");
@@ -507,63 +640,98 @@ void fxTMMoveOutTM(fxMesaContext fxMesa, struct gl_texture_object *tObj)
     exit(-1);
   }
 
-  ti->tmi.whichTMU=FX_TMU_NONE;
-  ti->tmi.isInTM=GL_FALSE;
+  ti->isInTM=GL_FALSE;
+  ti->whichTMU=FX_TMU_NONE;
 }
 
 void fxTMFreeTexture(fxMesaContext fxMesa, struct gl_texture_object *tObj)
 {
-  tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
+  tfxTexInfo *ti=fxTMGetTexInfo(tObj);
   int i;
 
-  fxTMMoveOutTM(fxMesa,tObj);
-
-  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);
+  fxTMMoveOutTM(fxMesa, tObj);
 
-    (void)ti->tmi.mipmapLevel[i].data;
+  for (i=0; i<MAX_TEXTURE_LEVELS; i++) {
+    if (ti->mipmapLevel[i].data) {
+      FREE(ti->mipmapLevel[i].data);
+      ti->mipmapLevel[i].data = 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 fxTMFreeAllFreeNode(tfxTMFreeNode *fn)
+void fxTMInit(fxMesaContext fxMesa)
 {
-  if(!fn)
-    return;
+  fxMesa->texBindNumber=0;
+  fxMesa->tmPool=0;
 
-  if(fn->next)
-    fxTMFreeAllFreeNode(fn->next);
+  fxTMUInit(fxMesa,FX_TMU0);
 
-  free(fn);
+  if(fxMesa->haveTwoTMUs)
+    fxTMUInit(fxMesa,FX_TMU1);
 }
 
-void fxTMFreeAllAllocNode(tfxTMAllocNode *an)
+void fxTMClose(fxMesaContext fxMesa)
 {
-  if(!an)
-    return;
-
-  if(an->next)
-    fxTMFreeAllAllocNode(an->next);
+  MemRange *tmp, *next;
 
-  free(an);
+  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)
-{
-  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;
+void
+fxTMRestoreTextures_NoLock(fxMesaContext ctx) {
+  tfxTexInfo *ti;
+  struct gl_texture_object *tObj;
+  int i, where;
+
+  tObj=ctx->glCtx->Shared->TexObjectList;
+  while (tObj) {
+    ti=fxTMGetTexInfo(tObj);
+    if (ti && ti->isInTM) {
+      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 */
+         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);
+    }
+    tObj=tObj->Next;
   }
 }
 
-
 #else