Removed savagedma.[ch]. They have been unused for a while and were only
[mesa.git] / src / mesa / drivers / dri / savage / savagetex.c
index dd8b4e89043358f0678f6344487393ca5b5f4bbe..568b5c4e7a5966511b6b447aab74cc852b3d8eb8 100644 (file)
 
 #include "xmlpool.h"
 
+/* On Savage4 the texure LOD-bias needs an offset of ~ 0.3 to get
+ * somewhere close to software rendering.
+ */
+#define SAVAGE4_LOD_OFFSET 10
+
 /* Size 1, 2 and 4 images are packed into the last subtile. Each image
  * is repeated to fill a 4x4 pixel area. The figure below shows the
  * layout of those 4x4 pixel areas in the 8x8 subtile.
@@ -78,6 +83,29 @@ static const savageTileInfo tileInfo_s3d_s4[5] = {
     {32, 16,  8, 2, 4, 8, {0xc0, 0x80}}, /* 32-bit */
 };
 
+/** \brief Template for subtile uploads.
+ * \param h   height in pixels
+ * \param w   width in bytes
+ */
+#define SUBTILE_FUNC(w,h)                                      \
+static __inline GLubyte *savageUploadSubtile_##w##x##h         \
+(GLubyte *dest, GLubyte *src, GLuint srcStride)                        \
+{                                                              \
+    GLuint y;                                                  \
+    for (y = 0; y < h; ++y) {                                  \
+       memcpy (dest, src, w);                                  \
+       src += srcStride;                                       \
+       dest += w;                                              \
+    }                                                          \
+    return dest;                                               \
+}
+
+SUBTILE_FUNC(2, 8) /* 4 bits per pixel, 4 pixels wide */
+SUBTILE_FUNC(4, 8)
+SUBTILE_FUNC(8, 8)
+SUBTILE_FUNC(16, 8)
+SUBTILE_FUNC(32, 8) /* 4 bytes per pixel, 8 pixels wide */
+
 /** \brief Upload a complete tile from src (srcStride) to dest
  *
  * \param tileInfo     Pointer to tiling information
@@ -102,16 +130,21 @@ static void savageUploadTile (const savageTileInfo *tileInfo,
                              GLubyte *src, GLuint srcStride, GLubyte *dest) {
     GLuint subStride = tileInfo->subWidth * bpp;
     GLubyte *srcSRow = src, *srcSTile = src;
-    GLuint sx, sy, y;
+    GLubyte *(*subtileFunc) (GLubyte *, GLubyte *, GLuint);
+    GLuint sx, sy;
+    switch (subStride) {
+    case  2: subtileFunc = savageUploadSubtile_2x8; break;
+    case  4: subtileFunc = savageUploadSubtile_4x8; break;
+    case  8: subtileFunc = savageUploadSubtile_8x8; break;
+    case 16: subtileFunc = savageUploadSubtile_16x8; break;
+    case 32: subtileFunc = savageUploadSubtile_32x8; break;
+    default: assert(0);
+    }
     for (sy = 0; sy < hInSub; ++sy) {
        srcSTile = srcSRow;
        for (sx = 0; sx < wInSub; ++sx) {
            src = srcSTile;
-           for (y = 0; y < tileInfo->subHeight; ++y) {
-               memcpy (dest, src, subStride);
-               src += srcStride;
-               dest += subStride;
-           }
+           dest = subtileFunc (dest, src, srcStride);
            srcSTile += subStride;
        }
        srcSRow += srcStride * tileInfo->subHeight;
@@ -186,9 +219,9 @@ static void savageUploadTiny (const savageTileInfo *tileInfo,
 
 /** \brief Upload an image from mesa's internal copy.
  */
-static void savageUploadTexLevel( savageTextureObjectPtr t, int level )
+static void savageUploadTexLevel( savageTexObjPtr t, int level )
 {
-    const struct gl_texture_image *image = t->image[level].image;
+    const struct gl_texture_image *image = t->base.tObj->Image[0][level];
     const savageTileInfo *tileInfo = t->tileInfo;
     GLuint width = image->Width2, height = image->Height2;
     GLuint bpp = t->texelBytes;
@@ -203,28 +236,67 @@ static void savageUploadTexLevel( savageTextureObjectPtr t, int level )
                 (int) image->Border);
 
     if (width >= 8 && height >= tileInfo->subHeight) {
+       GLuint *dirtyPtr = t->image[level].dirtyTiles;
+       GLuint dirtyMask = 1;
+
        if (width >= tileInfo->width && height >= tileInfo->height) {
            GLuint wInTiles = width / tileInfo->width;
            GLuint hInTiles = height / tileInfo->height;
            GLubyte *srcTRow = image->Data, *src;
-           GLubyte *dest = (GLubyte *)(t->BufAddr + t->image[level].offset);
+           GLubyte *dest = (GLubyte *)(t->bufAddr + t->image[level].offset);
            GLuint x, y;
            for (y = 0; y < hInTiles; ++y) {
                src = srcTRow;
                for (x = 0; x < wInTiles; ++x) {
-                   savageUploadTile (tileInfo,
-                                     tileInfo->wInSub, tileInfo->hInSub, bpp,
-                                     src, width * bpp, dest);
+                   if (*dirtyPtr & dirtyMask) {
+                       savageUploadTile (tileInfo,
+                                         tileInfo->wInSub, tileInfo->hInSub,
+                                         bpp, src, width * bpp, dest);
+                   }
                    src += tileInfo->width * bpp;
                    dest += 2048; /* tile size is always 2k */
+                   if (dirtyMask == 1<<31) {
+                       dirtyMask = 1;
+                       dirtyPtr++;
+                   } else
+                       dirtyMask <<= 1;
                }
                srcTRow += width * tileInfo->height * bpp;
            }
+       } else if (width >= tileInfo->width) {
+           GLuint wInTiles = width / tileInfo->width;
+           GLubyte *src = image->Data;
+           GLubyte *dest = (GLubyte *)(t->bufAddr + t->image[level].offset);
+           GLuint tileStride = tileInfo->width * bpp * height;
+           savageContextPtr imesa = (savageContextPtr)t->base.heap->driverContext;
+           GLuint x;
+           /* Savage3D-based chips seem so use a constant tile stride
+            * of 2048 for vertically incomplete tiles, but only if
+            * the color depth is 32bpp. Nobody said this was supposed
+            * to be logical!
+            */
+           if (bpp == 4 && imesa->savageScreen->chipset < S3_SAVAGE4)
+               tileStride = 2048;
+           for (x = 0; x < wInTiles; ++x) {
+               if (*dirtyPtr & dirtyMask) {
+                   savageUploadTile (tileInfo,
+                                     tileInfo->wInSub,
+                                     height / tileInfo->subHeight,
+                                     bpp, src, width * bpp, dest);
+               }
+               src += tileInfo->width * bpp;
+               dest += tileStride;
+               if (dirtyMask == 1<<31) {
+                   dirtyMask = 1;
+                   dirtyPtr++;
+               } else
+                   dirtyMask <<= 1;
+           }
        } else {
            savageUploadTile (tileInfo, width / tileInfo->subWidth,
                              height / tileInfo->subHeight, bpp,
                              image->Data, width * bpp,
-                             (GLubyte *)(t->BufAddr+t->image[level].offset));
+                             (GLubyte *)(t->bufAddr+t->image[level].offset));
        }
     } else {
        GLuint minHeight, minWidth, hRepeat, vRepeat, x, y;
@@ -241,7 +313,7 @@ static void savageUploadTexLevel( savageTextureObjectPtr t, int level )
            GLuint offset = y * tileInfo->subWidth*height * bpp;
            for (x = 0; x < hRepeat; ++x) {
                savageUploadTiny (tileInfo, width, height, bpp, image->Data,
-                                 (GLubyte *)(t->BufAddr +
+                                 (GLubyte *)(t->bufAddr +
                                              t->image[level].offset+offset));
                offset += width * bpp;
            }
@@ -270,48 +342,117 @@ static GLuint savageTexImageSize (GLuint width, GLuint height, GLuint bpp) {
        return 64 * bpp;
 }
 
-static void savageSetTexWrapping(savageTextureObjectPtr tex, GLenum s, GLenum t)
+/** \brief Compute the number of (partial) tiles of a texture image
+ */
+static GLuint savageTexImageTiles (GLuint width, GLuint height,
+                                  const savageTileInfo *tileInfo)
+{
+   return (width + tileInfo->width - 1) / tileInfo->width *
+      (height + tileInfo->height - 1) / tileInfo->height;
+}
+
+/** \brief Mark dirty tiles
+ *
+ * Some care must be taken because tileInfo may not be set or not
+ * up-to-date. So we check if tileInfo is initialized and if the number
+ * of tiles in the bit vector matches the number of tiles computed from
+ * the current tileInfo.
+ */
+static void savageMarkDirtyTiles (savageTexObjPtr t, GLuint level,
+                                 GLuint totalWidth, GLuint totalHeight,
+                                 GLint xoffset, GLint yoffset,
+                                 GLsizei width, GLsizei height)
+{
+   GLuint wInTiles, hInTiles;
+   GLuint x0, y0, x1, y1;
+   GLuint x, y;
+   if (!t->tileInfo)
+      return;
+   wInTiles = (totalWidth + t->tileInfo->width - 1) / t->tileInfo->width;
+   hInTiles = (totalHeight + t->tileInfo->height - 1) / t->tileInfo->height;
+   if (wInTiles * hInTiles != t->image[level].nTiles)
+      return;
+
+   x0 = xoffset / t->tileInfo->width;
+   y0 = yoffset / t->tileInfo->height;
+   x1 = (xoffset + width - 1) / t->tileInfo->width;
+   y1 = (yoffset + height - 1) / t->tileInfo->height;
+
+   for (y = y0; y <= y1; ++y) {
+      GLuint *ptr = t->image[level].dirtyTiles + (y * wInTiles + x0) / 32;
+      GLuint mask = 1 << (y * wInTiles + x0) % 32;
+      for (x = x0; x <= x1; ++x) {
+        *ptr |= mask;
+        if (mask == (1<<31)) {
+           ptr++;
+           mask = 1;
+        } else {
+           mask <<= 1;
+        }
+      }
+   }
+}
+
+/** \brief Mark all tiles as dirty
+ */
+static void savageMarkAllTiles (savageTexObjPtr t, GLuint level)
+{
+   GLuint words = (t->image[level].nTiles + 31) / 32;
+   if (words)
+      memset(t->image[level].dirtyTiles, ~0, words*sizeof(GLuint));
+}
+
+
+static void savageSetTexWrapping(savageTexObjPtr tex, GLenum s, GLenum t)
 {
-    tex->texParams.sWrapMode = s;
-    tex->texParams.tWrapMode = t;
+    tex->setup.sWrapMode = s;
+    tex->setup.tWrapMode = t;
 }
 
-static void savageSetTexFilter(savageTextureObjectPtr t, 
-                              GLenum minf, GLenum magf)
+static void savageSetTexFilter(savageTexObjPtr t, GLenum minf, GLenum magf)
 {
-   t->texParams.minFilter = minf;
-   t->texParams.magFilter = magf;
+   t->setup.minFilter = minf;
+   t->setup.magFilter = magf;
 }
 
 
 /* Need a fallback ?
  */
-static void savageSetTexBorderColor(savageTextureObjectPtr t, GLubyte color[4])
+static void savageSetTexBorderColor(savageTexObjPtr t, GLubyte color[4])
 {
 /*    t->Setup[SAVAGE_TEXREG_TEXBORDERCOL] =  */
-      t->texParams.boarderColor = SAVAGEPACKCOLOR8888(color[0],color[1],color[2],color[3]); 
+    /*t->setup.borderColor = SAVAGEPACKCOLOR8888(color[0],color[1],color[2],color[3]); */
 }
 
 
 
-static savageTextureObjectPtr
+static savageTexObjPtr
 savageAllocTexObj( struct gl_texture_object *texObj ) 
 {
-   savageTextureObjectPtr t;
+   savageTexObjPtr t;
 
-   t = (savageTextureObjectPtr) calloc(1,sizeof(*t));
+   t = (savageTexObjPtr) calloc(1,sizeof(*t));
    texObj->DriverData = t;
    if ( t != NULL ) {
+      GLuint i;
 
       /* Initialize non-image-dependent parts of the state:
        */
-      t->globj = texObj;
+      t->base.tObj = texObj;
+      t->base.dirty_images[0] = 0;
+      t->dirtySubImages = 0;
+      t->tileInfo = NULL;
+
+      /* Initialize dirty tiles bit vectors
+       */
+      for (i = 0; i < SAVAGE_TEX_MAXLEVELS; ++i)
+        t->image[i].nTiles = 0;
 
       /* FIXME Something here to set initial values for other parts of
        * FIXME t->setup?
        */
   
-      make_empty_list( t );
+      make_empty_list( &t->base );
 
       savageSetTexWrapping(t,texObj->WrapS,texObj->WrapT);
       savageSetTexFilter(t,texObj->MinFilter,texObj->MagFilter);
@@ -333,7 +474,6 @@ savageChooseTextureFormat( GLcontext *ctx, GLint internalFormat,
        ( imesa->texture_depth == DRI_CONF_TEXTURE_DEPTH_FORCE_16 );
    const GLboolean isSavage4 = (imesa->savageScreen->chipset >= S3_SAVAGE4);
    (void) format;
-   (void) type;
 
    switch ( internalFormat ) {
    case 4:
@@ -468,9 +608,10 @@ savageChooseTextureFormat( GLcontext *ctx, GLint internalFormat,
 static void savageSetTexImages( savageContextPtr imesa,
                                const struct gl_texture_object *tObj )
 {
-   savageTextureObjectPtr t = (savageTextureObjectPtr) tObj->DriverData;
+   savageTexObjPtr t = (savageTexObjPtr) tObj->DriverData;
    struct gl_texture_image *image = tObj->Image[0][tObj->BaseLevel];
    GLuint offset, i, textureFormat, size;
+   GLint firstLevel, lastLevel;
 
    assert(t);
    assert(image);
@@ -508,6 +649,7 @@ static void savageSetTexImages( savageContextPtr imesa,
       _mesa_problem(imesa->glCtx, "Bad texture format in %s", __FUNCTION__);
       return;
    }
+   t->hwFormat = textureFormat;
 
    /* Select tiling format depending on the chipset and bytes per texel */
    if (imesa->savageScreen->chipset <= S3_SAVAGE4)
@@ -515,311 +657,151 @@ static void savageSetTexImages( savageContextPtr imesa,
    else
        t->tileInfo = &tileInfo_pro[t->texelBytes];
 
-   /* Figure out the size now (and count the levels).  Upload won't be done
-    * until later.
+   /* Compute which mipmap levels we really want to send to the hardware.
+    */
+   driCalculateTextureFirstLastLevel( &t->base );
+   firstLevel = t->base.firstLevel;
+   lastLevel  = t->base.lastLevel;
+
+   /* Figure out the size now (and count the levels).  Upload won't be
+    * done until later. If the number of tiles changes, it means that
+    * this function is called for the first time on this tex object or
+    * the image or the destination color format changed. So all tiles
+    * are marked as dirty.
     */ 
-   t->dirty_images = 0;
    offset = 0;
    size = 1;
-   for ( i = 0 ; i < SAVAGE_TEX_MAXLEVELS && tObj->Image[0][i] ; i++ ) {
-      image = tObj->Image[0][i];
-      t->image[i].image = image;
+   for ( i = firstLevel ; i <= lastLevel && tObj->Image[0][i] ; i++ ) {
+      GLuint nTiles;
+      nTiles = savageTexImageTiles (image->Width2, image->Height2, t->tileInfo);
+      if (t->image[i].nTiles != nTiles) {
+        GLuint words = (nTiles + 31) / 32;
+        if (t->image[i].nTiles != 0) {
+           free(t->image[i].dirtyTiles);
+        }
+        t->image[i].dirtyTiles = malloc(words*sizeof(GLuint));
+        memset(t->image[i].dirtyTiles, ~0, words*sizeof(GLuint));
+      }
+      t->image[i].nTiles = nTiles;
+
       t->image[i].offset = offset;
-      t->image[i].internalFormat = textureFormat;
-      t->dirty_images |= (1<<i);
+
+      image = tObj->Image[0][i];
       size = savageTexImageSize (image->Width2, image->Height2,
                                 t->texelBytes);
       offset += size;
    }
 
-   t->totalSize = offset;
+   t->base.lastLevel = i-1;
+   t->base.totalSize = offset;
    /* the last three mipmap levels don't add to the offset. They are packed
     * into 64 pixels. */
    if (size == 0)
-       t->totalSize += 64 * t->texelBytes;
-   /* 2k-aligned */
-   t->totalSize = (t->totalSize + 2047UL) & ~2047UL;
-   t->max_level = i-1;
-   t->min_level = 0;
-}
-
-void savageDestroyTexObj(savageContextPtr imesa, savageTextureObjectPtr t)
-{
-   if (!t) return;
-
-   /* This is sad - need to sync *in case* we upload a texture
-    * to this newly free memory...
-    */
-   if (t->MemBlock) {
-      mmFreeMem(t->MemBlock);
-      t->MemBlock = 0;
-
-      if (t->age > imesa->dirtyAge)
-        imesa->dirtyAge = t->age;
-   }
-
-   if (t->globj)
-      t->globj->DriverData = 0;
-
-   remove_from_list(t);
-   free(t);
+       t->base.totalSize += 64 * t->texelBytes;
+   /* 2k-aligned (really needed?) */
+   t->base.totalSize = (t->base.totalSize + 2047UL) & ~2047UL;
 }
 
-
-static void savageSwapOutTexObj(savageContextPtr imesa, savageTextureObjectPtr t)
+void savageDestroyTexObj(savageContextPtr imesa, savageTexObjPtr t)
 {
-   if (t->MemBlock) {
-      mmFreeMem(t->MemBlock);
-      t->MemBlock = 0;      
-
-      if (t->age > imesa->dirtyAge)
-        imesa->dirtyAge = t->age;
-   }
-
-   t->dirty_images = ~0;
-   move_to_tail(&(imesa->SwappedOut), t);
-}
+    GLuint i;
 
+    /* Free dirty tiles bit vectors */
+    for (i = 0; i < SAVAGE_TEX_MAXLEVELS; ++i) {
+       if (t->image[i].nTiles)
+           free (t->image[i].dirtyTiles);
+    }
 
-
-void savagePrintLocalLRU( savageContextPtr imesa , GLuint heap) 
-{
-   savageTextureObjectPtr t;
-   int sz = 1 << (imesa->savageScreen->logTextureGranularity[heap]);
-   
-   foreach( t, &imesa->TexObjList[heap] ) {
-      if (!t->globj)
-        fprintf(stderr, "Placeholder %d at %x sz %x\n", 
-                t->MemBlock->ofs / sz,
-                t->MemBlock->ofs,
-                t->MemBlock->size);      
-      else
-        fprintf(stderr, "Texture (bound %d) at %x sz %x\n", 
-                t->bound,
-                t->MemBlock->ofs,
-                t->MemBlock->size);      
-
-   }
-}
-
-void savagePrintGlobalLRU( savageContextPtr imesa , GLuint heap)
-{
-   int i, j;
-
-   drm_tex_region_t *list = imesa->sarea->texList[heap];
-   
-
-   for (i = 0, j = SAVAGE_NR_TEX_REGIONS ; i < SAVAGE_NR_TEX_REGIONS ; i++) {
-      fprintf(stderr, "list[%d] age %d next %d prev %d\n",
-             j, list[j].age, list[j].next, list[j].prev);
-      j = list[j].next;
-      if (j == SAVAGE_NR_TEX_REGIONS) break;
-   }
-   
-   if (j != SAVAGE_NR_TEX_REGIONS)
-      fprintf(stderr, "Loop detected in global LRU\n");
-       for (i = 0 ; i < SAVAGE_NR_TEX_REGIONS ; i++) 
-       {
-          fprintf(stderr,"list[%d] age %d next %d prev %d\n",
-          i, list[i].age, list[i].next, list[i].prev);
-       }
-}
-
-
-void savageResetGlobalLRU( savageContextPtr imesa, GLuint heap )
-{
-    drm_tex_region_t *list = imesa->sarea->texList[heap];
-   int sz = 1 << imesa->savageScreen->logTextureGranularity[heap];
-   int i;
-
-   /* (Re)initialize the global circular LRU list.  The last element
-    * in the array (SAVAGE_NR_TEX_REGIONS) is the sentinal.  Keeping it
-    * at the end of the array allows it to be addressed rationally
-    * when looking up objects at a particular location in texture
-    * memory.  
-    */
-   for (i = 0 ; (i+1) * sz <= imesa->savageScreen->textureSize[heap]; i++) {
-      list[i].prev = i-1;
-      list[i].next = i+1;
-      list[i].age = 0;
-   }
-
-   i--;
-   list[0].prev = SAVAGE_NR_TEX_REGIONS;
-   list[i].prev = i-1;
-   list[i].next = SAVAGE_NR_TEX_REGIONS;
-   list[SAVAGE_NR_TEX_REGIONS].prev = i;
-   list[SAVAGE_NR_TEX_REGIONS].next = 0;
-   imesa->sarea->texAge[heap] = 0;
-}
-
-
-static void savageUpdateTexLRU( savageContextPtr imesa, savageTextureObjectPtr t ) 
-{
-   int i;
-   int heap = t->heap;
-   int logsz = imesa->savageScreen->logTextureGranularity[heap];
-   int start = t->MemBlock->ofs >> logsz;
-   int end = (t->MemBlock->ofs + t->MemBlock->size - 1) >> logsz;
-   drm_tex_region_t *list = imesa->sarea->texList[heap];
-   
-   imesa->texAge[heap] = ++imesa->sarea->texAge[heap];
-
-   /* Update our local LRU
-    */
-   move_to_head( &(imesa->TexObjList[heap]), t );
-
-   /* Update the global LRU
-    */
-   for (i = start ; i <= end ; i++) {
-
-      list[i].in_use = 1;
-      list[i].age = imesa->texAge[heap];
-
-      /* remove_from_list(i)
-       */
-      list[(unsigned)list[i].next].prev = list[i].prev;
-      list[(unsigned)list[i].prev].next = list[i].next;
-      
-      /* insert_at_head(list, i)
-       */
-      list[i].prev = SAVAGE_NR_TEX_REGIONS;
-      list[i].next = list[SAVAGE_NR_TEX_REGIONS].next;
-      list[(unsigned)list[SAVAGE_NR_TEX_REGIONS].next].prev = i;
-      list[SAVAGE_NR_TEX_REGIONS].next = i;
-   }
+    /* See if it was the driver's current object.
+     */
+    if ( imesa != NULL )
+    { 
+       for ( i = 0 ; i < imesa->glCtx->Const.MaxTextureUnits ; i++ )
+       {
+           if ( &t->base == imesa->CurrentTexObj[ i ] ) {
+               assert( t->base.bound & (1 << i) );
+               imesa->CurrentTexObj[ i ] = NULL;
+           }
+       }
+    }
 }
 
-
-/* Called for every shared texture region which has increased in age
- * since we last held the lock.
- *
- * Figures out which of our textures have been ejected by other clients,
- * and pushes a placeholder texture onto the LRU list to represent 
- * the other client's textures.  
+/* Upload a texture's images to one of the texture heaps. May have to
+ * eject our own and/or other client's texture objects to make room
+ * for the upload.
  */
-void savageTexturesGone( savageContextPtr imesa,
-                      GLuint heap,
-                      GLuint offset, 
-                      GLuint size,
-                      GLuint in_use ) 
+static void savageUploadTexImages( savageContextPtr imesa, savageTexObjPtr t )
 {
-   savageTextureObjectPtr t, tmp;
-   
-   foreach_s ( t, tmp, &imesa->TexObjList[heap] ) {
-
-      if (t->MemBlock->ofs >= offset + size ||
-         t->MemBlock->ofs + t->MemBlock->size <= offset)
-        continue;
-
-      /* It overlaps - kick it off.  Need to hold onto the currently bound
-       * objects, however.
-       */
-      if (t->bound)
-        savageSwapOutTexObj( imesa, t );
-      else
-        savageDestroyTexObj( imesa, t );
-   }
-
-   
-   if (in_use) {
-      t = (savageTextureObjectPtr) calloc(1,sizeof(*t));
-      if (!t) return;
-
-      t->heap = heap;
-      t->MemBlock = mmAllocMem( imesa->texHeap[heap], size, 0, offset);      
-      if(!t->MemBlock)
-      {
-          free(t);
-          return;
-      }
-      insert_at_head( &imesa->TexObjList[heap], t );
-   }
-}
-
-
-
+   const GLint numLevels = t->base.lastLevel - t->base.firstLevel + 1;
+   GLuint i;
 
+   assert(t);
 
-/* This is called with the lock held.  May have to eject our own and/or
- * other client's texture objects to make room for the upload.
- */
-int savageUploadTexImages( savageContextPtr imesa, savageTextureObjectPtr t )
-{
-   int heap;
-   int i;
-   int ofs;
+   LOCK_HARDWARE(imesa);
    
-   heap = t->heap = SAVAGE_CARD_HEAP;
-
    /* Do we need to eject LRU texture objects?
     */
-   if (!t->MemBlock) {
-      while (1)
-      {
-        t->MemBlock = mmAllocMem( imesa->texHeap[heap], t->totalSize, 12, 0 ); 
-        if (t->MemBlock)
-           break;
-        else if (imesa->lastTexHeap == 2)
-        {
-            heap = t->heap = SAVAGE_AGP_HEAP;
-            t->MemBlock = mmAllocMem( imesa->texHeap[heap], t->totalSize, 12, 0 ); 
-            
-            if (t->MemBlock)
-                break;
-        }
-
-        if (imesa->TexObjList[heap].prev->bound) {
-           fprintf(stderr, "Hit bound texture in upload\n"); 
-           savagePrintLocalLRU( imesa,heap );
-           return -1;
-        }
-
-        if (imesa->TexObjList[heap].prev == &(imesa->TexObjList[heap])) {
-           fprintf(stderr, "Failed to upload texture, sz %d\n", t->totalSize);
-           mmDumpMemInfo( imesa->texHeap[heap] );
-           return -1;
-        }
-        
-        savageSwapOutTexObj( imesa, imesa->TexObjList[heap].prev );
+   if (!t->base.memBlock) {
+      GLint heap;
+      GLuint ofs;
+
+      heap = driAllocateTexture(imesa->textureHeaps, imesa->lastTexHeap,
+                               (driTextureObject *)t);
+      if (heap == -1) {
+         UNLOCK_HARDWARE(imesa);
+         return;
       }
-      ofs = t->MemBlock->ofs;
-      t->texParams.hwPhysAddress = imesa->savageScreen->textureOffset[heap] + ofs;
-      t->BufAddr = (char *)((GLuint) imesa->savageScreen->texVirtual[heap] + ofs);
+
+      ofs = t->base.memBlock->ofs;
+      t->setup.physAddr = imesa->savageScreen->textureOffset[heap] + ofs;
+      t->bufAddr = (char *)((GLuint) imesa->savageScreen->texVirtual[heap] + ofs);
       imesa->dirty |= SAVAGE_UPLOAD_GLOBAL; /* FIXME: really needed? */
    }
 
    /* Let the world know we've used this memory recently.
     */
-   LOCK_HARDWARE(imesa);
-   savageUpdateTexLRU( imesa, t );
+   driUpdateTextureLRU( &t->base );
    UNLOCK_HARDWARE(imesa);
 
-   if (t->dirty_images) {
+   if (t->base.dirty_images[0] || t->dirtySubImages) {
+      if (SAVAGE_DEBUG & DEBUG_VERBOSE_TEX)
+        fprintf(stderr, "Texture upload: |");
+
       savageFlushVertices (imesa);
       LOCK_HARDWARE(imesa);
       savageFlushCmdBufLocked (imesa, GL_FALSE);
-      WAIT_IDLE_EMPTY_LOCKED(imesa);
-      if (SAVAGE_DEBUG & DEBUG_VERBOSE_LRU)
-        fprintf(stderr, "*");
+      /* Heap timestamps are only reliable with Savage DRM 2.3.x or
+       * later. Earlier versions had only 16 bit time stamps which
+       * would wrap too frequently. */
+      if (imesa->savageScreen->driScrnPriv->drmMinor >= 3) {
+         unsigned int heap = t->base.heap->heapId;
+         savageWaitEvent (imesa, imesa->textureHeaps[heap]->timestamp);
+      } else
+         WAIT_IDLE_EMPTY_LOCKED(imesa);
+
+      for (i = 0 ; i < numLevels ; i++) {
+         const GLint j = t->base.firstLevel + i;  /* the texObj's level */
+        if (t->base.dirty_images[0] & (1 << j)) {
+           savageMarkAllTiles(t, j);
+           if (SAVAGE_DEBUG & DEBUG_VERBOSE_TEX)
+               fprintf (stderr, "*");
+        } else if (SAVAGE_DEBUG & DEBUG_VERBOSE_TEX) {
+           if (t->dirtySubImages & (1 << j))
+              fprintf (stderr, ".");
+           else
+              fprintf (stderr, " ");
+        }
+        if ((t->base.dirty_images[0] | t->dirtySubImages) & (1 << j))
+           savageUploadTexLevel( t, j );
+      }
 
-      for (i = t->min_level ; i <= t->max_level ; i++)
-        if (t->dirty_images & (1<<i)) 
-           savageUploadTexLevel( t, i );
       UNLOCK_HARDWARE(imesa);
-   }
-
-
-   t->dirty_images = 0;
-   return 0;
-}
-
-static void savageTexSetUnit( savageTextureObjectPtr t, GLuint unit )
-{
-   if (t->current_unit == unit) return;
+      t->base.dirty_images[0] = 0;
+      t->dirtySubImages = 0;
 
-   t->current_unit = unit;
+      if (SAVAGE_DEBUG & DEBUG_VERBOSE_TEX)
+        fprintf(stderr, "|\n");
+   }
 }
 
 
@@ -829,7 +811,8 @@ static void savageUpdateTex0State_s4( GLcontext *ctx )
 {
    savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
    struct gl_texture_object    *tObj;
-   savageTextureObjectPtr t;
+   struct gl_texture_image *image;
+   savageTexObjPtr t;
    GLuint format;
 
    /* disable */
@@ -857,19 +840,15 @@ static void savageUpdateTex0State_s4( GLcontext *ctx )
          return;
    }
 
-   if (t->current_unit != 0)
-      savageTexSetUnit( t, 0 );
-    
-   imesa->CurrentTexObj[0] = t;
-   t->bound |= 1;
+   imesa->CurrentTexObj[0] = &t->base;
+   t->base.bound |= 1;
 
-   if (t->dirty_images) {
+   if (t->base.dirty_images[0] || t->dirtySubImages) {
        savageSetTexImages(imesa, tObj);
-       savageUploadTexImages(imesa, imesa->CurrentTexObj[0]); 
+       savageUploadTexImages(imesa, t); 
    }
    
-   if (t->MemBlock)
-      savageUpdateTexLRU( imesa, t );
+   driUpdateTextureLRU( &t->base );
 
    format = tObj->Image[0][tObj->BaseLevel]->Format;
 
@@ -1026,11 +1005,11 @@ static void savageUpdateTex0State_s4( GLcontext *ctx )
    }
 
     imesa->regs.s4.texCtrl[0].ni.uMode =
-       t->texParams.sWrapMode == GL_REPEAT ? 0 : 1;
+       t->setup.sWrapMode == GL_REPEAT ? 0 : 1;
     imesa->regs.s4.texCtrl[0].ni.vMode = 
-       t->texParams.tWrapMode == GL_REPEAT ? 0 : 1;
+       t->setup.tWrapMode == GL_REPEAT ? 0 : 1;
 
-    switch (t->texParams.minFilter)
+    switch (t->setup.minFilter)
     {
         case GL_NEAREST:
             imesa->regs.s4.texCtrl[0].ni.filterMode   = TFM_Point;
@@ -1062,7 +1041,8 @@ static void savageUpdateTex0State_s4( GLcontext *ctx )
     if((ctx->Texture.Unit[0].LodBias !=0.0F) ||
        (imesa->regs.s4.texCtrl[0].ni.dBias != 0))
     {
-       int bias = (int)(ctx->Texture.Unit[0].LodBias * 32.0);
+       int bias = (int)(ctx->Texture.Unit[0].LodBias * 32.0) +
+           SAVAGE4_LOD_OFFSET;
        if (bias < -256)
            bias = -256;
        else if (bias > 255)
@@ -1070,17 +1050,18 @@ static void savageUpdateTex0State_s4( GLcontext *ctx )
        imesa->regs.s4.texCtrl[0].ni.dBias = bias & 0x1ff;
     }
 
+    image = tObj->Image[0][tObj->BaseLevel];
     imesa->regs.s4.texDescr.ni.tex0En = GL_TRUE;
-    imesa->regs.s4.texDescr.ni.tex0Width  = t->image[0].image->WidthLog2;
-    imesa->regs.s4.texDescr.ni.tex0Height = t->image[0].image->HeightLog2;
-    imesa->regs.s4.texDescr.ni.tex0Fmt = t->image[0].internalFormat;
-    imesa->regs.s4.texCtrl[0].ni.dMax = t->max_level;
+    imesa->regs.s4.texDescr.ni.tex0Width  = image->WidthLog2;
+    imesa->regs.s4.texDescr.ni.tex0Height = image->HeightLog2;
+    imesa->regs.s4.texDescr.ni.tex0Fmt = t->hwFormat;
+    imesa->regs.s4.texCtrl[0].ni.dMax = t->base.lastLevel - t->base.firstLevel;
 
     if (imesa->regs.s4.texDescr.ni.tex1En)
         imesa->regs.s4.texDescr.ni.texBLoopEn = GL_TRUE;
 
-    imesa->regs.s4.texAddr[0].ui = (u_int32_t) t->texParams.hwPhysAddress | 0x2;
-    if(t->heap == SAVAGE_AGP_HEAP)
+    imesa->regs.s4.texAddr[0].ui = (u_int32_t) t->setup.physAddr | 0x2;
+    if(t->base.heap->heapId == SAVAGE_AGP_HEAP)
        imesa->regs.s4.texAddr[0].ui |= 0x1;
     
     return;
@@ -1089,7 +1070,8 @@ static void savageUpdateTex1State_s4( GLcontext *ctx )
 {
    savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
    struct gl_texture_object    *tObj;
-   savageTextureObjectPtr t;
+   struct gl_texture_image *image;
+   savageTexObjPtr t;
    GLuint format;
 
    /* disable */
@@ -1125,20 +1107,16 @@ static void savageUpdateTex1State_s4( GLcontext *ctx )
          return;
    }
     
-   if (t->current_unit != 1)
-      savageTexSetUnit( t, 1 );
-
-   imesa->CurrentTexObj[1] = t;
+   imesa->CurrentTexObj[1] = &t->base;
 
-   t->bound |= 2;
+   t->base.bound |= 2;
 
-   if (t->dirty_images) {
+   if (t->base.dirty_images[0] || t->dirtySubImages) {
        savageSetTexImages(imesa, tObj);
-       savageUploadTexImages(imesa, imesa->CurrentTexObj[1]);
+       savageUploadTexImages(imesa, t);
    }
    
-   if (t->MemBlock)
-      savageUpdateTexLRU( imesa, t );
+   driUpdateTextureLRU( &t->base );
 
    format = tObj->Image[0][tObj->BaseLevel]->Format;
 
@@ -1229,11 +1207,11 @@ static void savageUpdateTex1State_s4( GLcontext *ctx )
    }
 
     imesa->regs.s4.texCtrl[1].ni.uMode =
-       t->texParams.sWrapMode == GL_REPEAT ? 0 : 1;
+       t->setup.sWrapMode == GL_REPEAT ? 0 : 1;
     imesa->regs.s4.texCtrl[1].ni.vMode =
-       t->texParams.tWrapMode == GL_REPEAT ? 0 : 1;
+       t->setup.tWrapMode == GL_REPEAT ? 0 : 1;
 
-    switch (t->texParams.minFilter)
+    switch (t->setup.minFilter)
     {
         case GL_NEAREST:
             imesa->regs.s4.texCtrl[1].ni.filterMode   = TFM_Point;
@@ -1265,7 +1243,8 @@ static void savageUpdateTex1State_s4( GLcontext *ctx )
     if((ctx->Texture.Unit[1].LodBias !=0.0F) ||
        (imesa->regs.s4.texCtrl[1].ni.dBias != 0))
     {
-       int bias = (int)(ctx->Texture.Unit[1].LodBias * 32.0);
+       int bias = (int)(ctx->Texture.Unit[1].LodBias * 32.0) +
+           SAVAGE4_LOD_OFFSET;
        if (bias < -256)
            bias = -256;
        else if (bias > 255)
@@ -1273,22 +1252,24 @@ static void savageUpdateTex1State_s4( GLcontext *ctx )
        imesa->regs.s4.texCtrl[1].ni.dBias = bias & 0x1ff;
     }
 
+    image = tObj->Image[0][tObj->BaseLevel];
     imesa->regs.s4.texDescr.ni.tex1En = GL_TRUE;
-    imesa->regs.s4.texDescr.ni.tex1Width  = t->image[0].image->WidthLog2;
-    imesa->regs.s4.texDescr.ni.tex1Height = t->image[0].image->HeightLog2;
-    imesa->regs.s4.texDescr.ni.tex1Fmt = t->image[0].internalFormat;
-    imesa->regs.s4.texCtrl[1].ni.dMax = t->max_level;
+    imesa->regs.s4.texDescr.ni.tex1Width  = image->WidthLog2;
+    imesa->regs.s4.texDescr.ni.tex1Height = image->HeightLog2;
+    imesa->regs.s4.texDescr.ni.tex1Fmt = t->hwFormat;
+    imesa->regs.s4.texCtrl[1].ni.dMax = t->base.lastLevel - t->base.firstLevel;
     imesa->regs.s4.texDescr.ni.texBLoopEn = GL_TRUE;
 
-    imesa->regs.s4.texAddr[1].ui = (u_int32_t) t->texParams.hwPhysAddress| 2;
-    if(t->heap == SAVAGE_AGP_HEAP)
+    imesa->regs.s4.texAddr[1].ui = (u_int32_t) t->setup.physAddr | 2;
+    if(t->base.heap->heapId == SAVAGE_AGP_HEAP)
        imesa->regs.s4.texAddr[1].ui |= 0x1;
 }
 static void savageUpdateTexState_s3d( GLcontext *ctx )
 {
     savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
     struct gl_texture_object *tObj;
-    savageTextureObjectPtr t;
+    struct gl_texture_image *image;
+    savageTexObjPtr t;
     GLuint format;
 
     /* disable */
@@ -1316,19 +1297,15 @@ static void savageUpdateTexState_s3d( GLcontext *ctx )
            return;
     }
 
-    if (t->current_unit != 0)
-       savageTexSetUnit( t, 0 );
+    imesa->CurrentTexObj[0] = &t->base;
+    t->base.bound |= 1;
 
-    imesa->CurrentTexObj[0] = t;
-    t->bound |= 1;
-
-    if (t->dirty_images) {
+    if (t->base.dirty_images[0] || t->dirtySubImages) {
        savageSetTexImages(imesa, tObj);
-       savageUploadTexImages(imesa, imesa->CurrentTexObj[0]); 
+       savageUploadTexImages(imesa, t);
     }
 
-    if (t->MemBlock)
-       savageUpdateTexLRU( imesa, t );
+    driUpdateTextureLRU( &t->base );
 
     format = tObj->Image[0][tObj->BaseLevel]->Format;
 
@@ -1357,12 +1334,12 @@ static void savageUpdateTexState_s3d( GLcontext *ctx )
        truth. */
     imesa->regs.s3d.texCtrl.ni.uWrapEn = 0;
     imesa->regs.s3d.texCtrl.ni.vWrapEn = 0;
-    if (t->texParams.sWrapMode == GL_CLAMP)
+    if (t->setup.sWrapMode == GL_CLAMP)
        imesa->regs.s3d.texCtrl.ni.wrapMode = TAM_Clamp;
     else
        imesa->regs.s3d.texCtrl.ni.wrapMode = TAM_Wrap;
 
-    switch (t->texParams.minFilter) {
+    switch (t->setup.minFilter) {
     case GL_NEAREST:
        imesa->regs.s3d.texCtrl.ni.filterMode    = TFM_Point;
        imesa->regs.s3d.texCtrl.ni.mipmapDisable = GL_TRUE;
@@ -1413,14 +1390,15 @@ static void savageUpdateTexState_s3d( GLcontext *ctx )
        imesa->regs.s3d.texCtrl.ni.dBias = bias & 0x1ff;
     }
 
+    image = tObj->Image[0][tObj->BaseLevel];
     imesa->regs.s3d.texCtrl.ni.texEn = GL_TRUE;
-    imesa->regs.s3d.texDescr.ni.texWidth  = t->image[0].image->WidthLog2;
-    imesa->regs.s3d.texDescr.ni.texHeight = t->image[0].image->HeightLog2;
-    assert (t->image[0].internalFormat <= 7);
-    imesa->regs.s3d.texDescr.ni.texFmt = t->image[0].internalFormat;
+    imesa->regs.s3d.texDescr.ni.texWidth  = image->WidthLog2;
+    imesa->regs.s3d.texDescr.ni.texHeight = image->HeightLog2;
+    assert (t->hwFormat <= 7);
+    imesa->regs.s3d.texDescr.ni.texFmt = t->hwFormat;
 
-    imesa->regs.s3d.texAddr.ui = (u_int32_t) t->texParams.hwPhysAddress| 2;
-    if(t->heap == SAVAGE_AGP_HEAP)
+    imesa->regs.s3d.texAddr.ui = (u_int32_t) t->setup.physAddr | 2;
+    if(t->base.heap->heapId == SAVAGE_AGP_HEAP)
        imesa->regs.s3d.texAddr.ui |= 0x1;
 }
 
@@ -1429,6 +1407,15 @@ static void savageUpdateTexState_s3d( GLcontext *ctx )
 static void savageUpdateTextureState_s4( GLcontext *ctx )
 {
    savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
+
+   if ((imesa->CurrentTexObj[0] && ctx->Texture.Unit[0]._ReallyEnabled &&
+       ctx->Texture.Unit[0]._Current->DriverData != imesa->CurrentTexObj[0]) ||
+       (imesa->CurrentTexObj[1] && ctx->Texture.Unit[1]._ReallyEnabled &&
+       ctx->Texture.Unit[1]._Current->DriverData != imesa->CurrentTexObj[1]) ||
+       (imesa->CurrentTexObj[0] && !ctx->Texture.Unit[0]._ReallyEnabled) ||
+       (imesa->CurrentTexObj[1] && !ctx->Texture.Unit[1]._ReallyEnabled))
+       FLUSH_BATCH(imesa);
+
    if (imesa->CurrentTexObj[0]) imesa->CurrentTexObj[0]->bound &= ~1;
    if (imesa->CurrentTexObj[1]) imesa->CurrentTexObj[1]->bound &= ~2;
    imesa->CurrentTexObj[0] = 0;
@@ -1441,6 +1428,12 @@ static void savageUpdateTextureState_s4( GLcontext *ctx )
 static void savageUpdateTextureState_s3d( GLcontext *ctx )
 {
     savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
+
+    if ((imesa->CurrentTexObj[0] && ctx->Texture.Unit[0]._ReallyEnabled &&
+        ctx->Texture.Unit[0]._Current->DriverData != imesa->CurrentTexObj[0]) ||
+       (imesa->CurrentTexObj[0] && !ctx->Texture.Unit[0]._ReallyEnabled))
+       FLUSH_BATCH(imesa);
+
     if (imesa->CurrentTexObj[0]) imesa->CurrentTexObj[0]->bound &= ~1;
     imesa->CurrentTexObj[0] = 0;
     savageUpdateTexState_s3d( ctx );
@@ -1492,6 +1485,20 @@ static void savageTexEnv( GLcontext *ctx, GLenum target,
    } 
 }
 
+/* Update a heap's timestamp when a texture image is modified, so the
+ * new image is not uploaded while the old one is still in use.
+ */
+static void savageTexImageChanged (savageTexObjPtr t) {
+    /* Update the heap's time stamp, so the new image is not uploaded
+     * while the old one is still in use. */
+    if (t->base.heap) {
+       if (t->base.bound)
+           FLUSH_BATCH((savageContextPtr)t->base.heap->driverContext);
+       if (t->base.timestamp > t->base.heap->timestamp)
+           t->base.heap->timestamp = t->base.timestamp;
+    }
+}
+
 static void savageTexImage1D( GLcontext *ctx, GLenum target, GLint level,
                              GLint internalFormat,
                              GLint width, GLint border,
@@ -1500,9 +1507,9 @@ static void savageTexImage1D( GLcontext *ctx, GLenum target, GLint level,
                              struct gl_texture_object *texObj,
                              struct gl_texture_image *texImage )
 {
-   savageTextureObjectPtr t = (savageTextureObjectPtr) texObj->DriverData;
+   savageTexObjPtr t = (savageTexObjPtr) texObj->DriverData;
    if (t) {
-      savageSwapOutTexObj( SAVAGE_CONTEXT(ctx), t );
+      savageTexImageChanged (t);
    } else {
       t = savageAllocTexObj(texObj);
       if (!t) {
@@ -1513,7 +1520,7 @@ static void savageTexImage1D( GLcontext *ctx, GLenum target, GLint level,
    _mesa_store_teximage1d( ctx, target, level, internalFormat,
                           width, border, format, type,
                           pixels, packing, texObj, texImage );
-   t->dirty_images |= (1 << level);
+   t->base.dirty_images[0] |= (1 << level);
    SAVAGE_CONTEXT(ctx)->new_state |= SAVAGE_NEW_TEXTURE;
 }
 
@@ -1528,21 +1535,24 @@ static void savageTexSubImage1D( GLcontext *ctx,
                                 struct gl_texture_object *texObj,
                                 struct gl_texture_image *texImage )
 {
-   savageTextureObjectPtr t = (savageTextureObjectPtr) texObj->DriverData;
+   savageTexObjPtr t = (savageTexObjPtr) texObj->DriverData;
    assert( t ); /* this _should_ be true */
    if (t) {
-      savageSwapOutTexObj( SAVAGE_CONTEXT(ctx), t );
+      savageTexImageChanged (t);
+      savageMarkDirtyTiles(t, level, texImage->Width2, 1,
+                          xoffset, 0, width, 1);
    } else {
       t = savageAllocTexObj(texObj);
       if (!t) {
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage1D");
          return;
       }
+      t->base.dirty_images[0] |= (1 << level);
    }
    _mesa_store_texsubimage1d(ctx, target, level, xoffset, width, 
                             format, type, pixels, packing, texObj,
                             texImage);
-   t->dirty_images |= (1 << level);
+   t->dirtySubImages |= (1 << level);
    SAVAGE_CONTEXT(ctx)->new_state |= SAVAGE_NEW_TEXTURE;
 }
 
@@ -1554,9 +1564,9 @@ static void savageTexImage2D( GLcontext *ctx, GLenum target, GLint level,
                              struct gl_texture_object *texObj,
                              struct gl_texture_image *texImage )
 {
-   savageTextureObjectPtr t = (savageTextureObjectPtr) texObj->DriverData;
+   savageTexObjPtr t = (savageTexObjPtr) texObj->DriverData;
    if (t) {
-      savageSwapOutTexObj( SAVAGE_CONTEXT(ctx), t );
+      savageTexImageChanged (t);
    } else {
       t = savageAllocTexObj(texObj);
       if (!t) {
@@ -1567,7 +1577,7 @@ static void savageTexImage2D( GLcontext *ctx, GLenum target, GLint level,
    _mesa_store_teximage2d( ctx, target, level, internalFormat,
                           width, height, border, format, type,
                           pixels, packing, texObj, texImage );
-   t->dirty_images |= (1 << level);
+   t->base.dirty_images[0] |= (1 << level);
    SAVAGE_CONTEXT(ctx)->new_state |= SAVAGE_NEW_TEXTURE;
 }
 
@@ -1582,21 +1592,24 @@ static void savageTexSubImage2D( GLcontext *ctx,
                                 struct gl_texture_object *texObj,
                                 struct gl_texture_image *texImage )
 {
-   savageTextureObjectPtr t = (savageTextureObjectPtr) texObj->DriverData;
+   savageTexObjPtr t = (savageTexObjPtr) texObj->DriverData;
    assert( t ); /* this _should_ be true */
    if (t) {
-      savageSwapOutTexObj( SAVAGE_CONTEXT(ctx), t );
+      savageTexImageChanged (t);
+      savageMarkDirtyTiles(t, level, texImage->Width2, texImage->Height2,
+                          xoffset, yoffset, width, height);
    } else {
       t = savageAllocTexObj(texObj);
       if (!t) {
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage2D");
          return;
       }
+      t->base.dirty_images[0] |= (1 << level);
    }
    _mesa_store_texsubimage2d(ctx, target, level, xoffset, yoffset, width, 
                             height, format, type, pixels, packing, texObj,
                             texImage);
-   t->dirty_images |= (1 << level);
+   t->dirtySubImages |= (1 << level);
    SAVAGE_CONTEXT(ctx)->new_state |= SAVAGE_NEW_TEXTURE;
 }
 
@@ -1604,7 +1617,7 @@ static void savageTexParameter( GLcontext *ctx, GLenum target,
                              struct gl_texture_object *tObj,
                              GLenum pname, const GLfloat *params )
 {
-   savageTextureObjectPtr t = (savageTextureObjectPtr) tObj->DriverData;
+   savageTexObjPtr t = (savageTexObjPtr) tObj->DriverData;
    savageContextPtr imesa = SAVAGE_CONTEXT( ctx );
 
    if (!t || (target != GL_TEXTURE_1D && target != GL_TEXTURE_2D))
@@ -1645,37 +1658,21 @@ static void savageBindTexture( GLcontext *ctx, GLenum target,
 
 static void savageDeleteTexture( GLcontext *ctx, struct gl_texture_object *tObj )
 {
-   savageTextureObjectPtr t = (savageTextureObjectPtr)tObj->DriverData;
+   driTextureObject *t = (driTextureObject *)tObj->DriverData;
    savageContextPtr imesa = SAVAGE_CONTEXT( ctx );
 
    if (t) {
-
       if (t->bound) {
-        imesa->CurrentTexObj[t->bound-1] = 0;
-        imesa->new_state |= SAVAGE_NEW_TEXTURE;
+        FLUSH_BATCH(imesa);
       }
 
-      savageDestroyTexObj(imesa,t);
-      tObj->DriverData=0;
+      driDestroyTextureObject(t);
    }
    /* Free mipmap images and the texture object itself */
    _mesa_delete_texture_object(ctx, tObj);
 }
 
 
-static GLboolean savageIsTextureResident( GLcontext *ctx, 
-                                       struct gl_texture_object *t )
-{
-   savageTextureObjectPtr mt;
-
-/*     LOCK_HARDWARE; */
-   mt = (savageTextureObjectPtr)t->DriverData;
-/*     UNLOCK_HARDWARE; */
-
-   return mt && mt->MemBlock;
-}
-
-
 static struct gl_texture_object *
 savageNewTextureObject( GLcontext *ctx, GLuint name, GLenum target )
 {
@@ -1697,6 +1694,6 @@ void savageDDInitTextureFuncs( struct dd_function_table *functions )
    functions->BindTexture = savageBindTexture;
    functions->NewTextureObject = savageNewTextureObject;
    functions->DeleteTexture = savageDeleteTexture;
-   functions->IsTextureResident = savageIsTextureResident;
+   functions->IsTextureResident = driIsTextureResident;
    functions->TexParameter = savageTexParameter;
 }