In gl_texture_image replace IntFormat with InternalFormat and Format with
[mesa.git] / src / mesa / drivers / dri / savage / savagetex.c
index e2a28ae50ae046e276ca55e5da80442bb1e5d079..b2802904bdadaefd702f6a542eb502baabbc945b 100644 (file)
 #include "texstore.h"
 #include "texobj.h"
 
-#include "swrast/swrast.h"
-
-/* declarations of static and inline functions */
-__inline GLuint GetTiledCoordinates8(GLuint iBufferWidth, GLint x, GLint y);
-static GLuint GetTiledCoordinates16_4( GLint iBufferWidth,GLint x,GLint y );
-static GLuint GetTiledCoordinates16_8( GLint iBufferWidth,GLint x,GLint y );
-static GLuint GetTiledCoordinates32_4( GLint iBufferWidth, GLint x,   GLint y );
-static GLuint GetTiledCoordinates32_8( GLint iBufferWidth, GLint x,   GLint y );
-__inline GLuint GetTiledCoordinates( GLint iDepth,GLint iBufferWidth,GLint x,GLint y );
-__inline void savageUploadImage(savageTextureObjectPtr t, GLuint level, GLuint startx, GLuint starty, GLuint reloc);
-__inline void  savageTileTex(savageTextureObjectPtr tex, GLuint level);
-
-/* tile sizes depending on texel color depth */
-GLuint gTileWidth[5] =
-{
-    64,    /* 4-bit */ 
-    64,    /* 8-bit */
-    64,    /* 16-bit */
-    0,     /* 24-bit */
-    32     /* 32-bit */
-};
-
-GLuint gTileHeight[5] = 
-{
-    64,   /* 4-bit */
-    32,   /* 8-bit */
-    16,   /* 16-bit */
-    0,    /* 24-bit */
-    16    /* 32-bit */
-};
-
-__inline GLuint GetTiledCoordinates8(GLuint iBufferWidth, GLint x, GLint y)
-{
-    GLint x10, x106, x52;
-    GLint y20, y105, y43;
-    GLuint uWidthInTiles;
-
-    uWidthInTiles = (iBufferWidth + 63) >> 6;
-    x10 = x & 0x3;
-    x52 = (x & 0x3c) >> 2;
-    x106 = (x & 0x7c0) >> 6;
-
-    y20 = y & 0x7;
-    y43 = (y & 0x18) >> 3;
-    y105 = (y & 0x7e0) >> 5;
-    
-    return ( x10         |
-            (y20 << 2)   |
-           (x52 << 5)   |
-           (y43 << 9)   |
-           ((y105 * uWidthInTiles) + x106) << 11 );
-}
-
-/* 4-pixel wide subtiles */
-static GLuint GetTiledCoordinates16_4( GLint iBufferWidth,
-                                              GLint x,
-                                              GLint y )
-{
-    GLint  x106;
-    GLint  x10;
-    GLint  x52;
-    GLint  y104;
-    GLint  y20;
-    GLint  y3;
-    GLuint uiWidthInTiles;
-
-    /*
-    // calculating tiled address
-    */
+#include "convolve.h"
+#include "colormac.h"
 
-    uiWidthInTiles = (iBufferWidth + 63) >> 6;
+#include "swrast/swrast.h"
 
-    x10  =  x & 0x3;
-    x52  = (x & 0x3c ) >> 2;
-    x106 = (x & 0x7c0) >> 6;
+#include "xmlpool.h"
 
-    y20  =  y & 0x7;
-    y3   = (y & 8    ) >> 3;
-    y104 = (y & 0x7f0) >> 4;
+#define TILE_INDEX_DXT1 0
+#define TILE_INDEX_8    1
+#define TILE_INDEX_16   2
+#define TILE_INDEX_DXTn 3
+#define TILE_INDEX_32   4
 
-    return( (x10 << 1)  |
-            (y20 << 3)  |
-            (x52 << 6)  |
-            (y3  << 10) |
-            ((y104 * uiWidthInTiles) + x106) << 11 );
-}
-/* 8-pixel wide subtiles */
-static GLuint GetTiledCoordinates16_8( GLint iBufferWidth,
-                                              GLint x,
-                                              GLint y )
-{
-    GLint  x106;
-    GLint  x20;
-    GLint  x53;
-    GLint  y104;
-    GLint  y20;
-    GLint  y3;
-    GLuint uiWidthInTiles;
-
-    /*
-    // calculating tiled address
-    */
+/* On Savage4 the texure LOD-bias needs an offset of ~ 0.3 to get
+ * somewhere close to software rendering.
+ */
+#define SAVAGE4_LOD_OFFSET 10
 
-    uiWidthInTiles = (iBufferWidth + 63) >> 6;
+/* Tile info for S3TC formats counts in 4x4 blocks instead of texels.
+ * In DXT1 each block is encoded in 64 bits. In DXT3 and 5 each block is
+ * encoded in 128 bits. */
 
-    x20  =  x & 0x7;
-    x53  = (x & 0x38 ) >> 3;
-    x106 = (x & 0x7c0) >> 6;
+/* 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.
+ *
+ *    4 2
+ *    x 1
+ *
+ * Yuck! 8-bit texture formats use 4x8 subtiles. See below.
+ */
+static const savageTileInfo tileInfo_pro[5] = {
+    {16, 16, 16, 8, 1, 2, {0x18, 0x10}}, /* DXT1 */
+    {64, 32, 16, 4, 4, 8, {0x30, 0x20}}, /* 8-bit */
+    {64, 16,  8, 2, 8, 8, {0x48, 0x08}}, /* 16-bit */
+    {16,  8, 16, 4, 1, 2, {0x30, 0x20}}, /* DXT3, DXT5 */
+    {32, 16,  4, 2, 8, 8, {0x90, 0x10}}, /* 32-bit */
+};
 
-    y20  =  y & 0x7;
-    y3   = (y & 8    ) >> 3;
-    y104 = (y & 0x7f0) >> 4;
+/* Size 1, 2 and 4 images are packed into the last two subtiles. Each
+ * image is repeated to fill a 4x4 pixel area. The figures below show
+ * the layout of those 4x4 pixel areas in the two 4x8 subtiles.
+ *
+ * second last subtile: 4   last subtile: 2
+ *                      x                 1
+ */
+static const savageTileInfo tileInfo_s3d_s4[5] = {
+    {16, 16, 16, 8, 1, 2, {0x18, 0x10}}, /* DXT1 */
+    {64, 32, 16, 4, 4, 8, {0x30, 0x20}}, /* 8-bit */
+    {64, 16, 16, 2, 4, 8, {0x60, 0x40}}, /* 16-bit */
+    {16,  8, 16, 4, 1, 2, {0x30, 0x20}}, /* DXT3, DXT5 */
+    {32, 16,  8, 2, 4, 8, {0xc0, 0x80}}, /* 32-bit */
+};
 
-    return( (x20 << 1)  |
-            (y20 << 4)  |
-            (x53 << 7)  |
-            (y3  << 10) |
-            ((y104 * uiWidthInTiles) + x106) << 11 );
+/** \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;                                               \
 }
-/* function pointer set to the correct version in savageDDInitTextureFuncs */
-GLuint (*GetTiledCoordinates16) (GLint, GLint, GLint);
-
-/* 4-pixel wide subtiles */
-static GLuint GetTiledCoordinates32_4( GLint iBufferWidth,
-                                              GLint x,
-                                              GLint y )
-{
-    GLint  x10;
-    GLint  y20;
-    GLuint uiWidthInTiles;
-    GLint  x42;
-    GLint  x105;
-    GLint  y3;
-    GLint  y104;
-
-    /*
-    // calculating tiled address
-    */
-
-    uiWidthInTiles = (iBufferWidth + 31) >> 5;
 
-    x10  =  x & 0x3;
-    x42  = (x & 0x1c ) >> 2;
-    x105 = (x & 0x7e0) >> 5;
+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 */
 
-    y20  =  y & 0x7;
-    y3   = (y & 8    ) >> 3;
-    y104 = (y & 0x7f0) >> 4;
+SUBTILE_FUNC(8, 2) /* DXT1 */
+SUBTILE_FUNC(16, 2) /* DXT3 and DXT5 */
 
-    return( (x10 << 2)  |
-            (y20 << 4)  |
-            (x42 << 7)  |
-            (y3  << 10) |
-            ((y104 * uiWidthInTiles) + x105) << 11 );
+/** \brief Upload a complete tile from src (srcStride) to dest
+ *
+ * \param tileInfo     Pointer to tiling information
+ * \param wInSub       Width of source/dest image in subtiles
+ * \param hInSub       Height of source/dest image in subtiles
+ * \param bpp          Bytes per pixel
+ * \param src          Pointer to source data
+ * \param srcStride    Byte stride of rows in the source data
+ * \param dest         Pointer to destination
+ *
+ * Writes linearly to the destination memory in order to exploit write
+ * combining.
+ *
+ * For a complete tile wInSub and hInSub are set to the same values as
+ * in tileInfo. If the source image is smaller than a whole tile in
+ * one or both dimensions then they are set to the values of the
+ * source image. This only works as long as the source image is bigger
+ * than 8x8 pixels.
+ */
+static void savageUploadTile (const savageTileInfo *tileInfo,
+                             GLuint wInSub, GLuint hInSub, GLuint bpp,
+                             GLubyte *src, GLuint srcStride, GLubyte *dest) {
+    GLuint subStride = tileInfo->subWidth * bpp;
+    GLubyte *srcSRow = src, *srcSTile = src;
+    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 = tileInfo->subHeight == 8 ?
+                savageUploadSubtile_8x8 : savageUploadSubtile_8x2; break;
+    case 16: subtileFunc = tileInfo->subHeight == 8 ?
+                savageUploadSubtile_16x8 : savageUploadSubtile_16x2; 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;
+           dest = subtileFunc (dest, src, srcStride);
+           srcSTile += subStride;
+       }
+       srcSRow += srcStride * tileInfo->subHeight;
+    }
 }
-/* 8-pixel wide subtiles */
-static GLuint GetTiledCoordinates32_8( GLint iBufferWidth,
-                                              GLint x,
-                                              GLint y )
-{
-    GLint  x20;
-    GLint  y20;
-    GLuint uiWidthInTiles;
-    GLint  x43;
-    GLint  x105;
-    GLint  y3;
-    GLint  y104;
-
-    /*
-    // calculating tiled address
-    */
-
-    uiWidthInTiles = (iBufferWidth + 31) >> 5;
 
-    x20  =  x & 0x7;
-    x43  = (x & 0x18 ) >> 3;
-    x105 = (x & 0x7e0) >> 5;
-
-    y20  =  y & 0x7;
-    y3   = (y & 8    ) >> 3;
-    y104 = (y & 0x7f0) >> 4;
-
-    return( (x20 << 2)  |
-            (y20 << 5)  |
-            (x43 << 8)  |
-            (y3  << 10) |
-            ((y104 * uiWidthInTiles) + x105) << 11 );
+/** \brief Upload a image that is smaller than 8 pixels in either dimension.
+ *
+ * \param tileInfo    Pointer to tiling information
+ * \param width       Width of the image
+ * \param height      Height of the image
+ * \param bpp         Bytes per pixel
+ * \param src         Pointer to source data
+ * \param dest        Pointer to destination
+ *
+ * This function handles all the special cases that need to be taken
+ * care off. The caller may need to call this function multiple times
+ * with the destination offset in different ways since small texture
+ * images must be repeated in order to fill a whole tile (or 4x4 for
+ * the last 3 levels).
+ *
+ * FIXME: Repeating inside this function would be more efficient.
+ */
+static void savageUploadTiny (const savageTileInfo *tileInfo,
+                             GLuint pixWidth, GLuint pixHeight,
+                             GLuint width, GLuint height, GLuint bpp,
+                             GLubyte *src, GLubyte *dest) {
+    GLuint size = MAX2(pixWidth, pixHeight);
+
+    if (width > tileInfo->subWidth) { /* assert: height <= subtile height */
+       GLuint wInSub = width / tileInfo->subWidth;
+       GLuint srcStride = width * bpp;
+       GLuint subStride = tileInfo->subWidth * bpp;
+       GLuint subSkip = (tileInfo->subHeight - height) * subStride;
+       GLubyte *srcSTile = src;
+       GLuint sx, y;
+       for (sx = 0; sx < wInSub; ++sx) {
+           src = srcSTile;
+           for (y = 0; y < height; ++y) {
+               memcpy (dest, src, subStride);
+               src += srcStride;
+               dest += subStride;
+           }
+           dest += subSkip;
+           srcSTile += subStride;
+       }
+    } else if (size > 4) { /* a tile or less wide, except the last 3 levels */
+       GLuint srcStride = width * bpp;
+       GLuint subStride = tileInfo->subWidth * bpp;
+       /* if the subtile width is 4 we have to skip every other subtile */
+       GLuint subSkip = tileInfo->subWidth <= 4 ?
+           subStride * tileInfo->subHeight : 0;
+       GLuint skipRemainder = tileInfo->subHeight - 1;
+       GLuint y;
+       for (y = 0; y < height; ++y) {
+           memcpy (dest, src, srcStride);
+           src += srcStride;
+           dest += subStride;
+           if ((y & skipRemainder) == skipRemainder)
+               dest += subSkip;
+       }
+    } else { /* the last 3 mipmap levels */
+       GLuint offset = (size <= 2 ? tileInfo->tinyOffset[size-1] : 0);
+       GLuint subStride = tileInfo->subWidth * bpp;
+       GLuint y;
+       dest += offset;
+       for (y = 0; y < height; ++y) {
+           memcpy (dest, src, bpp*width);
+           src += width * bpp;
+           dest += subStride;
+       }
+    }
 }
-/* function pointer set to the correct version in savageDDInitTextureFuncs */
-GLuint (*GetTiledCoordinates32) (GLint, GLint, GLint);
 
-__inline GLuint GetTiledCoordinates( GLint iDepth,
-                                            GLint iBufferWidth,
-                                            GLint x,
-                                            GLint y )
+/** \brief Upload an image from mesa's internal copy.
+ */
+static void savageUploadTexLevel( savageTexObjPtr t, int level )
 {
-    /*
-    // don't check for 4 since we only have 3 types of fb
-    */
-
-    if (iDepth == 16)
-    {
-        return( GetTiledCoordinates16( iBufferWidth, x, y ) );
-    }
-    else if (iDepth == 32)
-    {
-        return( GetTiledCoordinates32( iBufferWidth, x, y ) );
-    }
-    else
-    {
-        return( GetTiledCoordinates8( iBufferWidth, x, y ) );
+    const struct gl_texture_image *image = t->base.tObj->Image[0][level];
+    const savageTileInfo *tileInfo = t->tileInfo;
+    GLuint pixWidth = image->Width2, pixHeight = image->Height2;
+    GLuint bpp = t->texelBytes;
+    GLuint width, height;
+
+    /* FIXME: Need triangle (rather than pixel) fallbacks to simulate
+     * this using normal textured triangles.
+     *
+     * DO THIS IN DRIVER STATE MANAGMENT, not hardware state.
+     */
+    if(image->Border != 0) 
+       fprintf (stderr, "Not supported texture border %d.\n",
+                (int) image->Border);
+
+    if (t->hwFormat == TFT_S3TC4A4Bit || t->hwFormat == TFT_S3TC4CA4Bit ||
+       t->hwFormat == TFT_S3TC4Bit) {
+       width = (pixWidth+3) / 4;
+       height = (pixHeight+3) / 4;
+    } else {
+       width = pixWidth;
+       height = pixHeight;
     }
-} 
-
-__inline void savageUploadImage(savageTextureObjectPtr t, GLuint level, GLuint startx, GLuint starty, GLuint reloc)
-{
-    GLuint uMaxTileWidth = gTileWidth[t->texelBytes]; 
-    GLuint x, y, w, row, col;    
-    const struct gl_texture_image *image = t->image[level].image;
-    GLubyte * dst, * src, * pBuffer;
-    GLint xAdd, yAdd;
-    GLuint uRowSeparator, uChunk = MIN_TILE_CHUNK, uWrap;
 
-  
-    pBuffer = (GLubyte *)(t->BufAddr + t->image[level].offset);
-    src = (GLubyte *)image->Data;
-    x = startx;
-    y = starty;
-    w = image->Width;
-
-    if(image->Format ==  GL_COLOR_INDEX)
-    {
-        if(w < MIN_TILE_CHUNK)
-       {
-            w = MIN_TILE_CHUNK;
-        }
-        else
-        {
-           if((w > 64 ) && (image->Height <= 16))
-            {
-                reloc = GL_TRUE;
-                 if(image->Height == 16)
-                {
-                    uChunk = MIN_TILE_CHUNK << 1;
-                }
+    if (pixWidth >= 8 && pixHeight >= 8) {
+       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);
+           GLuint x, y;
+           for (y = 0; y < hInTiles; ++y) {
+               src = srcTRow;
+               for (x = 0; x < wInTiles; ++x) {
+                   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;
            }
-       }  
-
-       if(!reloc & (w > (64 / 2)))
-       {
-           for(row = 0; row < image->Height; row++)
-           {
-               for(col = 0; col < image->Width; col++)    
-               {
-                   dst = pBuffer + GetTiledCoordinates(t->texelBytes << 3, w, x + col, y + row);
-                   memcpy (dst, src, t->texelBytes);
-                   src += t->texelBytes;
-                }
+       } 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));
        }
-        else
-        {
-           if(reloc & (w > 64))
-           {
-             uWrap = ((w + 63) >> 6) - 1;
-               for(row = 0; row < image->Height; row++)
-               {
-                   for(col = 0; col < image->Width; col++)    
-                   {
-                       xAdd = (col / (4 * 64)) * 64 + col % 64;
-                        yAdd = row + ((col / 64) & 3) * uChunk;
-                       dst = pBuffer + GetTiledCoordinates(t->texelBytes << 3, 64, x + xAdd, y + yAdd);
-                       memcpy (dst, src, t->texelBytes);
-                       src += t->texelBytes;
-                    }
-               }
-           }
-            else
-           {
-               uRowSeparator = 64 * MIN_TILE_CHUNK / w;
-               for(row = 0; row < image->Height; row++)
-               {
-                   xAdd = (w * (row / MIN_TILE_CHUNK)) % 64;
-                    yAdd = row % MIN_TILE_CHUNK + MIN_TILE_CHUNK * (row / uRowSeparator);
-                   for(col = 0; col < image->Width; col++)    
-                   {
-                       dst = pBuffer + GetTiledCoordinates(t->texelBytes << 3, w, x + xAdd, y + yAdd);
-                       memcpy (dst, src, t->texelBytes);
-                       src += t->texelBytes;
-                    }
-               }
-           }
-        }
-    }
-    else
-    {
-        if(w < MIN_TILE_CHUNK)
-       {
-            w = MIN_TILE_CHUNK;
-        }
-        else
-        {
-           if((w > uMaxTileWidth ) && (image->Height <= 8))
-            {
-                reloc = GL_TRUE;
-           }
+    } else {
+       GLuint minHeight, minWidth, hRepeat, vRepeat, x, y;
+       if (t->hwFormat == TFT_S3TC4A4Bit || t->hwFormat == TFT_S3TC4CA4Bit ||
+           t->hwFormat == TFT_S3TC4Bit)
+           minWidth = minHeight = 1;
+       else
+           minWidth = minHeight = 4;
+       if (width > minWidth || height > minHeight) {
+           minWidth = tileInfo->subWidth;
+           minHeight = tileInfo->subHeight;
        }
-       if(!reloc & (w > uMaxTileWidth / 2))
-       {
-           for(row = 0; row < image->Height; row++)
-           {
-               for(col = 0; col < image->Width; col++)    
-               {
-                   dst = pBuffer + GetTiledCoordinates(t->texelBytes << 3, w, x + col, y + row);
-                   memcpy (dst, src, t->texelBytes);
-                   src += t->texelBytes;
-                }
+       hRepeat = width  >= minWidth  ? 1 : minWidth  / width;
+       vRepeat = height >= minHeight ? 1 : minHeight / height;
+       for (y = 0; y < vRepeat; ++y) {
+           GLuint offset = y * tileInfo->subWidth*height * bpp;
+           for (x = 0; x < hRepeat; ++x) {
+               savageUploadTiny (tileInfo, pixWidth, pixHeight,
+                                 width, height, bpp, image->Data,
+                                 (GLubyte *)(t->bufAddr +
+                                             t->image[level].offset+offset));
+               offset += width * bpp;
            }
        }
-        else
-        {
-           if(reloc & (w > uMaxTileWidth))
-           {
-               for(row = 0; row < image->Height; row++)
-               {
-                   for(col = 0; col < image->Width; col++)    
-                   {
-                       xAdd = (col / (2 * uMaxTileWidth)) * uMaxTileWidth + col % uMaxTileWidth;
-                        yAdd = row + ((col / uMaxTileWidth) & 1) * MIN_TILE_CHUNK;
-                       dst = pBuffer + GetTiledCoordinates(t->texelBytes << 3, w, x + xAdd, y + yAdd);
-                       memcpy (dst, src, t->texelBytes);
-                       src += t->texelBytes;
-                    }
-               }
-           }
-            else
-           {
-               uRowSeparator = uMaxTileWidth * MIN_TILE_CHUNK / w;
-               for(row = 0; row < image->Height; row++)
-               {
-                    yAdd = row % MIN_TILE_CHUNK + MIN_TILE_CHUNK * (row / uRowSeparator);
-                    xAdd = (w * (row / MIN_TILE_CHUNK)) % uMaxTileWidth;
-                   for(col = 0; col < image->Width; col++)    
-                   {
-                       dst = pBuffer + GetTiledCoordinates(t->texelBytes << 3, w, x + col + xAdd, y + yAdd);
-                       memcpy (dst, src, t->texelBytes);
-                       src += t->texelBytes;
-                    }
-               }
-           }
-        }
     }
-}        
+}
 
+/** \brief Compute the destination size of a texture image
+ */
+static GLuint savageTexImageSize (GLuint width, GLuint height, GLuint bpp) {
+    /* full subtiles */
+    if (width >= 8 && height >= 8)
+       return width * height * bpp;
+    /* special case for the last three mipmap levels: the hardware computes
+     * the offset internally */
+    else if (width <= 4 && height <= 4)
+       return 0;
+    /* partially filled sub tiles waste memory
+     * on Savage3D and Savage4 with subtile width 4 every other subtile is
+     * skipped if width < 8 so we can assume a uniform subtile width of 8 */
+    else if (width >= 8)
+       return width * 8 * bpp;
+    else if (height >= 8)
+       return 8 * height * bpp;
+    else
+       return 64 * bpp;
+}
 
+/** \brief Compute the destination size of a compressed texture image
+ */
+static GLuint savageCompressedTexImageSize (GLuint width, GLuint height,
+                                           GLuint bpp) {
+    width = (width+3) / 4;
+    height = (height+3) / 4;
+    /* full subtiles */
+    if (width >= 2 && height >= 2)
+       return width * height * bpp;
+    /* special case for the last three mipmap levels: the hardware computes
+     * the offset internally */
+    else if (width <= 1 && height <= 1)
+       return 0;
+    /* partially filled sub tiles waste memory
+     * on Savage3D and Savage4 with subtile width 4 every other subtile is
+     * skipped if width < 8 so we can assume a uniform subtile width of 8 */
+    else if (width >= 2)
+       return width * 2 * bpp;
+    else if (height >= 2)
+       return 2 * height * bpp;
+    else
+       return 4 * bpp;
+}
 
-__inline void  savageTileTex(savageTextureObjectPtr tex, GLuint level)
+/** \brief Compute the number of (partial) tiles of a texture image
+ */
+static GLuint savageTexImageTiles (GLuint width, GLuint height,
+                                  const savageTileInfo *tileInfo)
 {
-    GLuint uWidth, uHeight;
-    GLint  xOffset, yOffset;
-    GLint  xStart=0, yStart=0;
-    GLint  minSize;
-    GLuint xRepeat, yRepeat;
-    GLuint startCol, startRow;
-    GLuint reloc;
-
-    const struct gl_texture_image *image = tex->image[level].image;
-    
-    reloc = GL_FALSE;
-    uWidth = image->Width2;
-    uHeight = image->Height2;
+   return (width + tileInfo->width - 1) / tileInfo->width *
+      (height + tileInfo->height - 1) / tileInfo->height;
+}
 
-    if((uWidth > 4) || (uHeight > 4))
-        minSize = MIN_TILE_CHUNK;
-    else
-        minSize = MIPMAP_CHUNK;
-    if(image->Width >= minSize)
-        xRepeat = 1;
-    else
-        xRepeat = minSize / image->Width;
+/** \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;
 
-    if(image->Height >= minSize)
-        yRepeat = 1;
-    else
-    {
-        yRepeat = minSize / image->Height;
-        if(minSize == MIN_TILE_CHUNK)
-            reloc = GL_TRUE;
-    }
-  
-    if(((uWidth < 4) && (uHeight < 4)) && (tex->texelBytes >= 2))
-    { 
-        if((uWidth == 2) || (uHeight == 2))
-        {
-            xStart = 4;
-            yStart = 0;
-        }
+   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;
+        }
+      }
+   }
+}
 
-        else
-        {
-            xStart = 4;
-            yStart = 4;
-        }
-    }
-    for(xOffset = 0; xOffset < xRepeat; xOffset++)
-    {
-        for(yOffset = 0; yOffset < yRepeat; yOffset++)
-        {
-           startCol = image->Width * xOffset + xStart;
-            startRow = image->Height * yOffset + yStart;
-            savageUploadImage(tex,level, startCol, startRow, reloc);
-       }
-    }
+/** \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(savageTextureObjectPtr tex, GLenum s, GLenum t)
+
+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);
@@ -509,17 +519,184 @@ savageAllocTexObj( struct gl_texture_object *texObj )
    return t;
 }
 
+/* Mesa texture formats for alpha-images on Savage3D/IX/MX
+ *
+ * Promoting texture images to ARGB888 or ARGB4444 doesn't work
+ * because we can't tell the hardware to ignore the color components
+ * and only use the alpha component. So we define our own texture
+ * formats that promote to ARGB8888 or ARGB4444 and set the color
+ * components to white. This way we get the correct result. */
+static GLboolean
+_savage_texstore_a1114444 (GLcontext *ctx, GLuint dims,
+                          GLenum baseInternalFormat,
+                          const struct gl_texture_format *dstFormat,
+                          GLvoid *dstAddr,
+                          GLint dstXoffset, GLint dstYoffset, GLint dstZoffset,
+                          GLint dstRowStride, GLint dstImageStride,
+                          GLint srcWidth, GLint srcHeight, GLint srcDepth,
+                          GLenum srcFormat, GLenum srcType,
+                          const GLvoid *srcAddr,
+                          const struct gl_pixelstore_attrib *srcPacking);
+static GLboolean
+_savage_texstore_a1118888 (GLcontext *ctx, GLuint dims,
+                          GLenum baseInternalFormat,
+                          const struct gl_texture_format *dstFormat,
+                          GLvoid *dstAddr,
+                          GLint dstXoffset, GLint dstYoffset, GLint dstZoffset,
+                          GLint dstRowStride, GLint dstImageStride,
+                          GLint srcWidth, GLint srcHeight, GLint srcDepth,
+                          GLenum srcFormat, GLenum srcType,
+                          const GLvoid *srcAddr,
+                          const struct gl_pixelstore_attrib *srcPacking);
+
+static struct gl_texture_format _savage_texformat_a1114444 = {
+    MESA_FORMAT_ARGB4444,              /* MesaFormat */
+    GL_RGBA,                           /* BaseFormat */
+    GL_UNSIGNED_NORMALIZED_ARB,                /* DataType */
+    4,                                 /* RedBits */
+    4,                                 /* GreenBits */
+    4,                                 /* BlueBits */
+    4,                                 /* AlphaBits */
+    0,                                 /* LuminanceBits */
+    0,                                 /* IntensityBits */
+    0,                                 /* IndexBits */
+    0,                                 /* DepthBits */
+    0,                                 /* StencilBits */
+    2,                                 /* TexelBytes */
+    _savage_texstore_a1114444,         /* StoreTexImageFunc */
+    NULL, NULL, NULL, NULL, NULL, NULL  /* FetchTexel* filled in by 
+                                        * savageDDInitTextureFuncs */
+};
+static struct gl_texture_format _savage_texformat_a1118888 = {
+    MESA_FORMAT_ARGB8888,              /* MesaFormat */
+    GL_RGBA,                           /* BaseFormat */
+    GL_UNSIGNED_NORMALIZED_ARB,                /* DataType */
+    8,                                 /* RedBits */
+    8,                                 /* GreenBits */
+    8,                                 /* BlueBits */
+    8,                                 /* AlphaBits */
+    0,                                 /* LuminanceBits */
+    0,                                 /* IntensityBits */
+    0,                                 /* IndexBits */
+    0,                                 /* DepthBits */
+    0,                                 /* StencilBits */
+    4,                                 /* TexelBytes */
+    _savage_texstore_a1118888,         /* StoreTexImageFunc */
+    NULL, NULL, NULL, NULL, NULL, NULL  /* FetchTexel* filled in by 
+                                        * savageDDInitTextureFuncs */
+};
+
+static GLboolean
+_savage_texstore_a1114444 (GLcontext *ctx, GLuint dims,
+                          GLenum baseInternalFormat,
+                          const struct gl_texture_format *dstFormat,
+                          GLvoid *dstAddr,
+                          GLint dstXoffset, GLint dstYoffset, GLint dstZoffset,
+                          GLint dstRowStride, GLint dstImageStride,
+                          GLint srcWidth, GLint srcHeight, GLint srcDepth,
+                          GLenum srcFormat, GLenum srcType,
+                          const GLvoid *srcAddr,
+                          const struct gl_pixelstore_attrib *srcPacking)
+{
+    /* general path */
+    const GLchan *tempImage = _mesa_make_temp_chan_image(ctx, dims,
+                                                 baseInternalFormat,
+                                                 baseInternalFormat,
+                                                 srcWidth, srcHeight, srcDepth,
+                                                 srcFormat, srcType, srcAddr,
+                                                 srcPacking);
+    const GLchan *src = tempImage;
+    GLubyte *dstImage = (GLubyte *) dstAddr
+       + dstZoffset * dstImageStride
+       + dstYoffset * dstRowStride
+       + dstXoffset * dstFormat->TexelBytes;
+    GLint img, row, col;
+
+    ASSERT(dstFormat == &_savage_texformat_a1114444);
+    ASSERT(baseInternalFormat == GL_ALPHA);
+
+    if (!tempImage)
+       return GL_FALSE;
+    _mesa_adjust_image_for_convolution(ctx, dims, &srcWidth, &srcHeight);
+    for (img = 0; img < srcDepth; img++) {
+       GLubyte *dstRow = dstImage;
+       for (row = 0; row < srcHeight; row++) {
+            GLushort *dstUI = (GLushort *) dstRow;
+           for (col = 0; col < srcWidth; col++) {
+               dstUI[col] = PACK_COLOR_4444( CHAN_TO_UBYTE(src[0]),
+                                             255, 255, 255 );
+               src += 1;
+            }
+            dstRow += dstRowStride;
+       }
+       dstImage += dstImageStride;
+    }
+    _mesa_free((void *) tempImage);
+
+    return GL_TRUE;
+}
+static GLboolean
+_savage_texstore_a1118888 (GLcontext *ctx, GLuint dims,
+                          GLenum baseInternalFormat,
+                          const struct gl_texture_format *dstFormat,
+                          GLvoid *dstAddr,
+                          GLint dstXoffset, GLint dstYoffset, GLint dstZoffset,
+                          GLint dstRowStride, GLint dstImageStride,
+                          GLint srcWidth, GLint srcHeight, GLint srcDepth,
+                          GLenum srcFormat, GLenum srcType,
+                          const GLvoid *srcAddr,
+                          const struct gl_pixelstore_attrib *srcPacking)
+{
+    /* general path */
+    const GLchan *tempImage = _mesa_make_temp_chan_image(ctx, dims,
+                                                 baseInternalFormat,
+                                                 baseInternalFormat,
+                                                 srcWidth, srcHeight, srcDepth,
+                                                 srcFormat, srcType, srcAddr,
+                                                 srcPacking);
+    const GLchan *src = tempImage;
+    GLubyte *dstImage = (GLubyte *) dstAddr
+       + dstZoffset * dstImageStride
+       + dstYoffset * dstRowStride
+       + dstXoffset * dstFormat->TexelBytes;
+    GLint img, row, col;
+
+    ASSERT(dstFormat == &_savage_texformat_a1118888);
+    ASSERT(baseInternalFormat == GL_ALPHA);
+
+    if (!tempImage)
+       return GL_FALSE;
+    _mesa_adjust_image_for_convolution(ctx, dims, &srcWidth, &srcHeight);
+    for (img = 0; img < srcDepth; img++) {
+       GLubyte *dstRow = dstImage;
+       for (row = 0; row < srcHeight; row++) {
+            GLuint *dstUI = (GLuint *) dstRow;
+           for (col = 0; col < srcWidth; col++) {
+               dstUI[col] = PACK_COLOR_8888( CHAN_TO_UBYTE(src[0]),
+                                             255, 255, 255 );
+               src += 1;
+            }
+            dstRow += dstRowStride;
+       }
+       dstImage += dstImageStride;
+    }
+    _mesa_free((void *) tempImage);
+
+    return GL_TRUE;
+}
+
 /* Called by the _mesa_store_teximage[123]d() functions. */
 static const struct gl_texture_format *
 savageChooseTextureFormat( GLcontext *ctx, GLint internalFormat,
                           GLenum format, GLenum type )
 {
    savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
-   const GLboolean do32bpt = GL_FALSE;
-   const GLboolean force16bpt = GL_FALSE;
+   const GLboolean do32bpt =
+       ( imesa->texture_depth == DRI_CONF_TEXTURE_DEPTH_32 );
+   const GLboolean force16bpt =
+       ( 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:
@@ -587,14 +764,14 @@ savageChooseTextureFormat( GLcontext *ctx, GLint internalFormat,
    case GL_ALPHA:
    case GL_COMPRESSED_ALPHA:
       return isSavage4 ? &_mesa_texformat_a8 : (
-        do32bpt ? &_mesa_texformat_argb8888 : &_mesa_texformat_argb4444);
+        do32bpt ? &_savage_texformat_a1118888 : &_savage_texformat_a1114444);
    case GL_ALPHA4:
-      return isSavage4 ? &_mesa_texformat_a8 : &_mesa_texformat_argb4444;
+      return isSavage4 ? &_mesa_texformat_a8 : &_savage_texformat_a1114444;
    case GL_ALPHA8:
    case GL_ALPHA12:
    case GL_ALPHA16:
       return isSavage4 ? &_mesa_texformat_a8 : (
-        !force16bpt ? &_mesa_texformat_argb8888 : &_mesa_texformat_argb4444);
+        !force16bpt ? &_savage_texformat_a1118888 : &_savage_texformat_a1114444);
 
    case 1:
    case GL_LUMINANCE:
@@ -623,7 +800,9 @@ savageChooseTextureFormat( GLcontext *ctx, GLint internalFormat,
    case GL_LUMINANCE12_ALPHA12:
    case GL_LUMINANCE16_ALPHA16:
       return !force16bpt ? &_mesa_texformat_argb8888 : &_mesa_texformat_argb4444;
-
+#if 0
+   /* TFT_I8 produces garbage on ProSavageDDR and subsequent texture
+    * disable keeps rendering garbage. Disabled for now. */
    case GL_INTENSITY:
    case GL_COMPRESSED_INTENSITY:
       return isSavage4 ? &_mesa_texformat_i8 : (
@@ -635,6 +814,38 @@ savageChooseTextureFormat( GLcontext *ctx, GLint internalFormat,
    case GL_INTENSITY16:
       return isSavage4 ? &_mesa_texformat_i8 : (
         !force16bpt ? &_mesa_texformat_argb8888 : &_mesa_texformat_argb4444);
+#else
+   case GL_INTENSITY:
+   case GL_COMPRESSED_INTENSITY:
+      return do32bpt ? &_mesa_texformat_argb8888 : &_mesa_texformat_argb4444;
+   case GL_INTENSITY4:
+      return &_mesa_texformat_argb4444;
+   case GL_INTENSITY8:
+   case GL_INTENSITY12:
+   case GL_INTENSITY16:
+      return !force16bpt ? &_mesa_texformat_argb8888 :
+         &_mesa_texformat_argb4444;
+#endif
+
+   case GL_RGB_S3TC:
+   case GL_RGB4_S3TC:
+   case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+      return &_mesa_texformat_rgb_dxt1;
+   case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+      return &_mesa_texformat_rgba_dxt1;
+
+   case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+      return &_mesa_texformat_rgba_dxt3;
+
+   case GL_RGBA_S3TC:
+   case GL_RGBA4_S3TC:
+      if (!isSavage4)
+        /* Not the best choice but Savage3D/MX/IX don't support DXT3 or DXT5. */
+        return &_mesa_texformat_rgba_dxt1;
+      /* fall through */
+   case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+      return &_mesa_texformat_rgba_dxt5;
+
 /*
    case GL_COLOR_INDEX:
    case GL_COLOR_INDEX1_EXT:
@@ -654,9 +865,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, width, pitch, i, textureFormat, log_pitch;
+   GLuint offset, i, textureFormat, tileIndex, size;
+   GLint firstLevel, lastLevel;
 
    assert(t);
    assert(image);
@@ -664,378 +876,311 @@ static void savageSetTexImages( savageContextPtr imesa,
    switch (image->TexFormat->MesaFormat) {
    case MESA_FORMAT_ARGB8888:
       textureFormat = TFT_ARGB8888;
-      t->texelBytes = 4;
+      t->texelBytes = tileIndex = 4;
       break;
    case MESA_FORMAT_ARGB1555:
       textureFormat = TFT_ARGB1555;
-      t->texelBytes = 2;
+      t->texelBytes = tileIndex = 2;
       break;
    case MESA_FORMAT_ARGB4444:
       textureFormat = TFT_ARGB4444;
-      t->texelBytes = 2;
+      t->texelBytes = tileIndex = 2;
       break;
    case MESA_FORMAT_RGB565:
       textureFormat = TFT_RGB565;
-      t->texelBytes = 2;
+      t->texelBytes = tileIndex = 2;
       break;
    case MESA_FORMAT_L8:
       textureFormat = TFT_L8;
-      t->texelBytes = 1;
+      t->texelBytes = tileIndex = 1;
       break;
    case MESA_FORMAT_I8:
       textureFormat = TFT_I8;
-      t->texelBytes = 1;
+      t->texelBytes = tileIndex = 1;
       break;
    case MESA_FORMAT_A8:
       textureFormat = TFT_A8;
-      t->texelBytes = 1;
+      t->texelBytes = tileIndex = 1;
+      break;
+   case MESA_FORMAT_RGB_DXT1:
+      textureFormat = TFT_S3TC4Bit;
+      tileIndex = TILE_INDEX_DXT1;
+      t->texelBytes = 8;
+      break;
+   case MESA_FORMAT_RGBA_DXT1:
+      textureFormat = TFT_S3TC4Bit;
+      tileIndex = TILE_INDEX_DXT1;
+      t->texelBytes = 8;
+      break;
+   case MESA_FORMAT_RGBA_DXT3:
+      textureFormat =  TFT_S3TC4A4Bit;
+      tileIndex = TILE_INDEX_DXTn;
+      t->texelBytes = 16;
+      break;
+   case MESA_FORMAT_RGBA_DXT5:
+      textureFormat = TFT_S3TC4CA4Bit;
+      tileIndex = TILE_INDEX_DXTn;
+      t->texelBytes = 16;
       break;
    default:
       _mesa_problem(imesa->glCtx, "Bad texture format in %s", __FUNCTION__);
+      return;
    }
+   t->hwFormat = textureFormat;
 
-   /* Figure out the size now (and count the levels).  Upload won't be done
-    * until later.
-    */ 
-   width = image->Width * t->texelBytes;
-   for (pitch = 2, log_pitch=1 ; pitch < width ; pitch *= 2 )
-      log_pitch++;
-   
-   t->dirty_images = 0;
+   /* Select tiling format depending on the chipset and texture format */
+   if (imesa->savageScreen->chipset <= S3_SAVAGE4)
+       t->tileInfo = &tileInfo_s3d_s4[tileIndex];
+   else
+       t->tileInfo = &tileInfo_pro[tileIndex];
 
+   /* 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.
+    */ 
    offset = 0;
-   for ( i = 0 ; i < SAVAGE_TEX_MAXLEVELS && tObj->Image[0][i] ; i++ ) {
-      t->image[i].image = tObj->Image[0][i];
-      t->image[i].offset = offset;
-      t->image[i].internalFormat = textureFormat;
-      t->dirty_images |= (1<<i);
-      offset += t->image[i].image->Height * pitch;
-      pitch = pitch >> 1;
-   }
-
-   t->totalSize = offset;
-   t->max_level = i-1;
-   t->min_level = 0;
-}
-
-void savageDestroyTexObj(savageContextPtr imesa, savageTextureObjectPtr t)
-{
-   if (!t) return;
+   size = 1;
+   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;
 
-   /* 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;
+      t->image[i].offset = offset;
 
-      if (t->age > imesa->dirtyAge)
-        imesa->dirtyAge = t->age;
+      image = tObj->Image[0][i];
+      if (t->texelBytes >= 8)
+        size = savageCompressedTexImageSize (image->Width2, image->Height2,
+                                             t->texelBytes);
+      else
+        size = savageTexImageSize (image->Width2, image->Height2,
+                                   t->texelBytes);
+      offset += size;
    }
 
-   if (t->globj)
-      t->globj->DriverData = 0;
-
-   remove_from_list(t);
-   free(t);
+   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->base.totalSize += (t->texelBytes >= 8 ? 4 : 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;      
+    GLuint i;
 
-      if (t->age > imesa->dirtyAge)
-        imesa->dirtyAge = t->age;
-   }
+    /* Free dirty tiles bit vectors */
+    for (i = 0; i < SAVAGE_TEX_MAXLEVELS; ++i) {
+       if (t->image[i].nTiles)
+           free (t->image[i].dirtyTiles);
+    }
 
-   t->dirty_images = ~0;
-   move_to_tail(&(imesa->SwappedOut), t);
+    /* 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;
+           }
+       }
+    }
 }
 
-
-
-/* Upload an image from mesa's internal copy.
+/* 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.
  */
-static void savageUploadTexLevel( savageTextureObjectPtr t, int level )
+static void savageUploadTexImages( savageContextPtr imesa, savageTexObjPtr t )
 {
-   const struct gl_texture_image *image = t->image[level].image;
-
-
-   /* Need triangle (rather than pixel) fallbacks to simulate this using
-    * normal textured triangles.
-    *
-    * DO THIS IN DRIVER STATE MANAGMENT, not hardware state.
-    * 
-    */
-
-   if(image->Border != 0) 
-       fprintf (stderr, "Not supported texture border %d.\n",
-               (int) image->Border);
-
-   savageTileTex(t, level);
-}
+   const GLint numLevels = t->base.lastLevel - t->base.firstLevel + 1;
+   GLuint i;
 
+   assert(t);
 
-
-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_savage_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;
-   }
+   LOCK_HARDWARE(imesa);
    
-   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_savage_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.  
+   /* Do we need to eject LRU texture objects?
     */
-   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;
-}
-
+   if (!t->base.memBlock) {
+      GLint heap;
+      GLuint ofs;
+
+      heap = driAllocateTexture(imesa->textureHeaps, imesa->lastTexHeap,
+                               (driTextureObject *)t);
+      if (heap == -1) {
+         UNLOCK_HARDWARE(imesa);
+         return;
+      }
 
-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_savage_tex_region_t *list = imesa->sarea->texList[heap];
-   
-   imesa->texAge[heap] = ++imesa->sarea->texAge[heap];
+      ofs = t->base.memBlock->ofs;
+      t->setup.physAddr = imesa->savageScreen->textureOffset[heap] + ofs;
+      t->bufAddr = (GLubyte *)imesa->savageScreen->texVirtual[heap] + ofs;
+      imesa->dirty |= SAVAGE_UPLOAD_GLOBAL; /* FIXME: really needed? */
+   }
 
-   /* Update our local LRU
+   /* Let the world know we've used this memory recently.
     */
-   move_to_head( &(imesa->TexObjList[heap]), t );
+   driUpdateTextureLRU( &t->base );
+   UNLOCK_HARDWARE(imesa);
+
+   if (t->base.dirty_images[0] || t->dirtySubImages) {
+      if (SAVAGE_DEBUG & DEBUG_VERBOSE_TEX)
+        fprintf(stderr, "Texture upload: |");
+
+      /* 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;
+         LOCK_HARDWARE(imesa);
+         savageWaitEvent (imesa, imesa->textureHeaps[heap]->timestamp);
+      } else {
+         savageFlushVertices (imesa);
+         LOCK_HARDWARE(imesa);
+         savageFlushCmdBufLocked (imesa, GL_FALSE);
+         WAIT_IDLE_EMPTY_LOCKED(imesa);
+      }
 
-   /* Update the global LRU
-    */
-   for (i = start ; i <= end ; i++) {
+      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 );
+      }
 
-      list[i].in_use = 1;
-      list[i].age = imesa->texAge[heap];
+      UNLOCK_HARDWARE(imesa);
+      t->base.dirty_images[0] = 0;
+      t->dirtySubImages = 0;
 
-      /* 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;
+      if (SAVAGE_DEBUG & DEBUG_VERBOSE_TEX)
+        fprintf(stderr, "|\n");
    }
 }
 
 
-/* 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.  
- */
-void savageTexturesGone( savageContextPtr imesa,
-                      GLuint heap,
-                      GLuint offset, 
-                      GLuint size,
-                      GLuint in_use ) 
+static void
+savage4_set_wrap_mode( savageContextPtr imesa, unsigned unit,
+                     GLenum s_mode, GLenum t_mode )
 {
-   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;
+    switch( s_mode ) {
+    case GL_REPEAT:
+       imesa->regs.s4.texCtrl[ unit ].ni.uMode = TAM_Wrap;
+       break;
+    case GL_CLAMP:
+    case GL_CLAMP_TO_EDGE:
+       imesa->regs.s4.texCtrl[ unit ].ni.uMode = TAM_Clamp;
+       break;
+    case GL_MIRRORED_REPEAT:
+       imesa->regs.s4.texCtrl[ unit ].ni.uMode = TAM_Mirror;
+       break;
+    }
 
-      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 );
-   }
+    switch( t_mode ) {
+    case GL_REPEAT:
+       imesa->regs.s4.texCtrl[ unit ].ni.vMode = TAM_Wrap;
+       break;
+    case GL_CLAMP:
+    case GL_CLAMP_TO_EDGE:
+       imesa->regs.s4.texCtrl[ unit ].ni.vMode = TAM_Clamp;
+       break;
+    case GL_MIRRORED_REPEAT:
+       imesa->regs.s4.texCtrl[ unit ].ni.vMode = TAM_Mirror;
+       break;
+    }
 }
 
 
-
-
-
-/* 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.
+/**
+ * Sets the hardware bits for the specified GL texture filter modes.
+ * 
+ * \todo
+ * Does the Savage4 have the ability to select the magnification filter?
  */
-int savageUploadTexImages( savageContextPtr imesa, savageTextureObjectPtr t )
+static void
+savage4_set_filter_mode( savageContextPtr imesa, unsigned unit,
+                        GLenum minFilter, GLenum magFilter )
 {
-   int heap;
-   int i;
-   int ofs;
-   
-   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
-        {
-            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;
-        }
-        
-        savageDestroyTexObj( imesa, imesa->TexObjList[heap].prev );
-      }
-      ofs = t->MemBlock->ofs;
-      t->texParams.hwPhysAddress = imesa->savageScreen->textureOffset[heap] + ofs;
-      t->BufAddr = (char *)((GLuint) imesa->savageScreen->texVirtual[heap] + ofs);
-      imesa->dirty |= SAVAGE_UPLOAD_CTX;
-   }
-
-   /* Let the world know we've used this memory recently.
-    */
-   savageUpdateTexLRU( imesa, t );
+    (void) magFilter;
 
-   if (t->dirty_images) {
-      if (SAVAGE_DEBUG & DEBUG_VERBOSE_LRU)
-        fprintf(stderr, "*");
-
-      for (i = t->min_level ; i <= t->max_level ; i++)
-        if (t->dirty_images & (1<<i)) 
-           savageUploadTexLevel( t, i );
-   }
+    switch (minFilter) {
+    case GL_NEAREST:
+       imesa->regs.s4.texCtrl[ unit ].ni.filterMode   = TFM_Point;
+       imesa->regs.s4.texCtrl[ unit ].ni.mipmapEnable = GL_FALSE;
+       break;
 
+    case GL_LINEAR:
+       imesa->regs.s4.texCtrl[ unit ].ni.filterMode   = TFM_Bilin;
+       imesa->regs.s4.texCtrl[ unit ].ni.mipmapEnable = GL_FALSE;
+       break;
 
-   t->dirty_images = 0;
-   return 0;
-}
+    case GL_NEAREST_MIPMAP_NEAREST:
+       imesa->regs.s4.texCtrl[ unit ].ni.filterMode   = TFM_Point;
+       imesa->regs.s4.texCtrl[ unit ].ni.mipmapEnable = GL_TRUE;
+       break;
 
-static void savageTexSetUnit( savageTextureObjectPtr t, GLuint unit )
-{
-   if (t->current_unit == unit) return;
+    case GL_LINEAR_MIPMAP_NEAREST:
+       imesa->regs.s4.texCtrl[ unit ].ni.filterMode   = TFM_Bilin;
+       imesa->regs.s4.texCtrl[ unit ].ni.mipmapEnable = GL_TRUE;
+       break;
 
-   t->current_unit = unit;
+    case GL_NEAREST_MIPMAP_LINEAR:
+    case GL_LINEAR_MIPMAP_LINEAR:
+       imesa->regs.s4.texCtrl[ unit ].ni.filterMode   = TFM_Trilin;
+       imesa->regs.s4.texCtrl[ unit ].ni.mipmapEnable = GL_TRUE;
+       break;
+    }
 }
 
 
-
-
 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 */
-   if (ctx->Texture.Unit[0]._ReallyEnabled == 0) {
-      imesa->regs.s4.texDescr.ni.tex0En = GL_FALSE;
-      imesa->regs.s4.texBlendCtrl[0].ui = TBC_NoTexMap;
-      imesa->regs.s4.texCtrl[0].ui = 0x20f040;
-      imesa->regs.s4.texAddr[0].ui = 0;
+   imesa->regs.s4.texDescr.ni.tex0En = GL_FALSE;
+   imesa->regs.s4.texBlendCtrl[0].ui = TBC_NoTexMap;
+   imesa->regs.s4.texCtrl[0].ui = 0x20f040;
+   if (ctx->Texture.Unit[0]._ReallyEnabled == 0)
       return;
-   }
 
    tObj = ctx->Texture.Unit[0]._Current;
-   if (ctx->Texture.Unit[0]._ReallyEnabled != TEXTURE_2D_BIT ||
-       tObj->Image[0][tObj->BaseLevel]->Border > 0) {
-      /* 1D or 3D texturing enabled, or texture border - fallback */
+   if ((ctx->Texture.Unit[0]._ReallyEnabled & ~(TEXTURE_1D_BIT|TEXTURE_2D_BIT))
+       || tObj->Image[0][tObj->BaseLevel]->Border > 0) {
+      /* 3D texturing enabled, or texture border - fallback */
       FALLBACK (ctx, SAVAGE_FALLBACK_TEXTURE, GL_TRUE);
       return;
    }
@@ -1049,21 +1194,17 @@ 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;
+   format = tObj->Image[0][tObj->BaseLevel]->_BaseFormat;
 
    switch (ctx->Texture.Unit[0].EnvMode) {
    case GL_REPLACE:
@@ -1125,6 +1266,7 @@ static void savageUpdateTex0State_s4( GLcontext *ctx )
         break;
 
     case GL_BLEND:
+       imesa->regs.s4.texBlendColor.ui = imesa->texEnvColor;
 
         switch (format)
         {
@@ -1194,13 +1336,28 @@ static void savageUpdateTex0State_s4( GLcontext *ctx )
                                      &imesa->regs.s4.texBlendCtrl[0]);
         break;
 
-        /*
-         GL_ADD
-        */
     case GL_ADD:
-        printf("Add\n");
         imesa->regs.s4.texCtrl[0].ni.clrArg1Invert = GL_FALSE;
-        imesa->regs.s4.texBlendCtrl[0].ui = TBC_AddAlpha;
+        switch (format)
+        {
+            case GL_ALPHA:
+                imesa->regs.s4.texBlendCtrl[0].ui = TBC_ModulAlpha;
+               break;
+
+            case GL_LUMINANCE:
+            case GL_RGB:
+               imesa->regs.s4.texBlendCtrl[0].ui = TBC_Add;
+               break;
+
+            case GL_LUMINANCE_ALPHA:
+            case GL_RGBA:
+               imesa->regs.s4.texBlendCtrl[0].ui = TBC_Add;
+               break;
+
+            case GL_INTENSITY:
+               imesa->regs.s4.texBlendCtrl[0].ui = TBC_AddAlpha;
+               break;
+       }
         __HWEnvCombineSingleUnitScale(imesa, 0, 0,
                                      &imesa->regs.s4.texBlendCtrl[0]);
         break;
@@ -1218,83 +1375,33 @@ static void savageUpdateTex0State_s4( GLcontext *ctx )
       break;                   
    }
 
-    imesa->regs.s4.texCtrl[0].ni.uMode = !(t->texParams.sWrapMode & 0x01);
-    imesa->regs.s4.texCtrl[0].ni.vMode = !(t->texParams.tWrapMode & 0x01);
+    savage4_set_wrap_mode( imesa, 0, t->setup.sWrapMode, t->setup.tWrapMode );
+    savage4_set_filter_mode( imesa, 0, t->setup.minFilter, t->setup.magFilter );
 
-    switch (t->texParams.minFilter)
-    {
-        case GL_NEAREST:
-            imesa->regs.s4.texCtrl[0].ni.filterMode   = TFM_Point;
-            imesa->regs.s4.texCtrl[0].ni.mipmapEnable = GL_FALSE;
-            break;
-
-        case GL_LINEAR:
-            imesa->regs.s4.texCtrl[0].ni.filterMode   = TFM_Bilin;
-            imesa->regs.s4.texCtrl[0].ni.mipmapEnable = GL_FALSE;
-            break;
-
-        case GL_NEAREST_MIPMAP_NEAREST:
-            imesa->regs.s4.texCtrl[0].ni.filterMode   = TFM_Point;
-            imesa->regs.s4.texCtrl[0].ni.mipmapEnable = GL_TRUE;
-            break;
-
-        case GL_LINEAR_MIPMAP_NEAREST:
-            imesa->regs.s4.texCtrl[0].ni.filterMode   = TFM_Bilin;
-            imesa->regs.s4.texCtrl[0].ni.mipmapEnable = GL_TRUE;
-            break;
-
-        case GL_NEAREST_MIPMAP_LINEAR:
-        case GL_LINEAR_MIPMAP_LINEAR:
-            imesa->regs.s4.texCtrl[0].ni.filterMode   = TFM_Trilin;
-            imesa->regs.s4.texCtrl[0].ni.mipmapEnable = GL_TRUE;
-            break;
-    }
-
-    if((ctx->Texture.Unit[0].LodBias !=0.0F) &&
+    if((ctx->Texture.Unit[0].LodBias !=0.0F) ||
        (imesa->regs.s4.texCtrl[0].ni.dBias != 0))
     {
-       union {
-           GLfloat f;
-           GLint i;
-       } bias;
-       GLuint  ul;
-       
-       bias.f = ctx->Texture.Unit[0].LodBias;
-       
-       /* if the value is >= 15.9375 determine whether >= 16
-          or <0
-       */
-       if(((bias.i) & 0x7FFFFFFF) >= 0x417F0000)
-       {
-           if((bias.i) & 0x80000000)
-           {
-               ul=0x101;
-           }
-           else
-           {
-               ul=0xff;
-           }
-       }
-       else
-       {
-            ul=(GLuint)(bias.f*16.0);
-        }
-        
-        ul &= 0x1FF;
-        imesa->regs.s4.texCtrl[0].ni.dBias = ul;
+       int bias = (int)(ctx->Texture.Unit[0].LodBias * 32.0) +
+           SAVAGE4_LOD_OFFSET;
+       if (bias < -256)
+           bias = -256;
+       else if (bias > 255)
+           bias = 255;
+       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 = (GLuint) 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;
@@ -1303,7 +1410,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 */
@@ -1313,20 +1421,18 @@ static void savageUpdateTex1State_s4( GLcontext *ctx )
        return;
    }
 
-   if (ctx->Texture.Unit[1]._ReallyEnabled == 0) {
-      imesa->regs.s4.texDescr.ni.tex1En = GL_FALSE;
-      imesa->regs.s4.texBlendCtrl[1].ui = TBC_NoTexMap1;
-      imesa->regs.s4.texCtrl[1].ui = 0x20f040;
-      imesa->regs.s4.texAddr[1].ui = 0;
-      imesa->regs.s4.texDescr.ni.texBLoopEn = GL_FALSE;
+   imesa->regs.s4.texDescr.ni.tex1En = GL_FALSE;
+   imesa->regs.s4.texBlendCtrl[1].ui = TBC_NoTexMap1;
+   imesa->regs.s4.texCtrl[1].ui = 0x20f040;
+   imesa->regs.s4.texDescr.ni.texBLoopEn = GL_FALSE;
+   if (ctx->Texture.Unit[1]._ReallyEnabled == 0)
       return;
-   }
 
    tObj = ctx->Texture.Unit[1]._Current;
 
-   if (ctx->Texture.Unit[1]._ReallyEnabled != TEXTURE_2D_BIT ||
-       tObj->Image[0][tObj->BaseLevel]->Border > 0) {
-      /* 1D or 3D texturing enabled, or texture border - fallback */
+   if ((ctx->Texture.Unit[1]._ReallyEnabled & ~(TEXTURE_1D_BIT|TEXTURE_2D_BIT))
+       || tObj->Image[0][tObj->BaseLevel]->Border > 0) {
+      /* 3D texturing enabled, or texture border - fallback */
       FALLBACK (ctx, SAVAGE_FALLBACK_TEXTURE, GL_TRUE);
       return;
    }
@@ -1340,22 +1446,18 @@ static void savageUpdateTex1State_s4( GLcontext *ctx )
          return;
    }
     
-   if (t->current_unit != 1)
-      savageTexSetUnit( t, 1 );
+   imesa->CurrentTexObj[1] = &t->base;
 
-   imesa->CurrentTexObj[1] = t;
+   t->base.bound |= 2;
 
-   t->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;
+   format = tObj->Image[0][tObj->BaseLevel]->_BaseFormat;
 
    switch (ctx->Texture.Unit[1].EnvMode) {
    case GL_REPLACE:
@@ -1385,13 +1487,30 @@ static void savageUpdateTex1State_s4( GLcontext *ctx )
        __HWEnvCombineSingleUnitScale(imesa, 0, 1, &imesa->regs.s4.texBlendCtrl);
        break;
 
-/*#if GL_EXT_texture_env_add*/
     case GL_ADD:
         imesa->regs.s4.texCtrl[1].ni.clrArg1Invert = GL_FALSE;
-        imesa->regs.s4.texBlendCtrl[1].ui = TBC_AddAlpha1;
+        switch (format)
+        {
+            case GL_ALPHA:
+                imesa->regs.s4.texBlendCtrl[1].ui = TBC_ModulAlpha1;
+               break;
+
+            case GL_LUMINANCE:
+            case GL_RGB:
+               imesa->regs.s4.texBlendCtrl[1].ui = TBC_Add1;
+               break;
+
+            case GL_LUMINANCE_ALPHA:
+            case GL_RGBA:
+               imesa->regs.s4.texBlendCtrl[1].ui = TBC_Add1;
+               break;
+
+            case GL_INTENSITY:
+               imesa->regs.s4.texBlendCtrl[1].ui = TBC_AddAlpha1;
+               break;
+       }
         __HWEnvCombineSingleUnitScale(imesa, 0, 1, &imesa->regs.s4.texBlendCtrl);
         break;
-/*#endif*/
 
 #if GL_ARB_texture_env_combine
     case GL_COMBINE_ARB:
@@ -1438,109 +1557,58 @@ static void savageUpdateTex1State_s4( GLcontext *ctx )
       break;
 
    default:
-      fprintf(stderr, "unkown tex 1 env mode\n");
+      fprintf(stderr, "unknown tex 1 env mode\n");
       exit(1);
       break;                   
    }
 
-    imesa->regs.s4.texCtrl[1].ni.uMode = !(t->texParams.sWrapMode & 0x01);
-    imesa->regs.s4.texCtrl[1].ni.vMode = !(t->texParams.tWrapMode & 0x01);
+    savage4_set_wrap_mode( imesa, 1, t->setup.sWrapMode, t->setup.tWrapMode );
+    savage4_set_filter_mode( imesa, 1, t->setup.minFilter, t->setup.magFilter );
 
-    switch (t->texParams.minFilter)
-    {
-        case GL_NEAREST:
-            imesa->regs.s4.texCtrl[1].ni.filterMode   = TFM_Point;
-            imesa->regs.s4.texCtrl[1].ni.mipmapEnable = GL_FALSE;
-            break;
-
-        case GL_LINEAR:
-            imesa->regs.s4.texCtrl[1].ni.filterMode   = TFM_Bilin;
-            imesa->regs.s4.texCtrl[1].ni.mipmapEnable = GL_FALSE;
-            break;
-
-        case GL_NEAREST_MIPMAP_NEAREST:
-            imesa->regs.s4.texCtrl[1].ni.filterMode   = TFM_Point;
-            imesa->regs.s4.texCtrl[1].ni.mipmapEnable = GL_TRUE;
-            break;
-
-        case GL_LINEAR_MIPMAP_NEAREST:
-            imesa->regs.s4.texCtrl[1].ni.filterMode   = TFM_Bilin;
-            imesa->regs.s4.texCtrl[1].ni.mipmapEnable = GL_TRUE;
-            break;
-
-        case GL_NEAREST_MIPMAP_LINEAR:
-        case GL_LINEAR_MIPMAP_LINEAR:
-            imesa->regs.s4.texCtrl[1].ni.filterMode   = TFM_Trilin;
-            imesa->regs.s4.texCtrl[1].ni.mipmapEnable = GL_TRUE;
-            break;
-    }
-    
-    if((ctx->Texture.Unit[1].LodBias !=0.0F)&&
+    if((ctx->Texture.Unit[1].LodBias !=0.0F) ||
        (imesa->regs.s4.texCtrl[1].ni.dBias != 0))
     {
-       union {
-           GLfloat f;
-           GLint i;
-       } bias;
-       GLuint  ul;
-       
-       bias.f = ctx->Texture.Unit[1].LodBias;
-       
-       /* if the value is >= 15.9375 determine whether >= 16
-          or <0
-       */
-       if(((bias.i) & 0x7FFFFFFF) >= 0x417F0000)
-       {
-           if((bias.i) & 0x80000000)
-           {
-               ul=0x101;
-           }
-           else
-           {
-               ul=0xff;
-           }
-       }
-       else
-       {
-            ul=(GLuint)(bias.f*16.0);
-        }
-        
-        ul &= 0x1FF;
-        imesa->regs.s4.texCtrl[1].ni.dBias = ul;
+       int bias = (int)(ctx->Texture.Unit[1].LodBias * 32.0) +
+           SAVAGE4_LOD_OFFSET;
+       if (bias < -256)
+           bias = -256;
+       else if (bias > 255)
+           bias = 255;
+       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 = (GLuint) 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 */
-    if (ctx->Texture.Unit[0]._ReallyEnabled == 0) {
-       imesa->regs.s3d.texCtrl.ui = 0;
-       imesa->regs.s3d.texCtrl.ni.texEn = GL_FALSE;
-       imesa->regs.s3d.texCtrl.ni.dBias = 0x08;
-       imesa->regs.s3d.texCtrl.ni.texXprEn = GL_TRUE;
-       imesa->regs.s3d.texAddr.ui = 0;
+    imesa->regs.s3d.texCtrl.ui = 0;
+    imesa->regs.s3d.texCtrl.ni.texEn = GL_FALSE;
+    imesa->regs.s3d.texCtrl.ni.dBias = 0x08;
+    imesa->regs.s3d.texCtrl.ni.texXprEn = GL_TRUE;
+    if (ctx->Texture.Unit[0]._ReallyEnabled == 0)
        return;
-    }
 
     tObj = ctx->Texture.Unit[0]._Current;
-    if (ctx->Texture.Unit[0]._ReallyEnabled != TEXTURE_2D_BIT ||
-       tObj->Image[0][tObj->BaseLevel]->Border > 0) {
-       /* 1D or 3D texturing enabled, or texture border - fallback */
+    if ((ctx->Texture.Unit[0]._ReallyEnabled & ~(TEXTURE_1D_BIT|TEXTURE_2D_BIT))
+       || tObj->Image[0][tObj->BaseLevel]->Border > 0) {
+       /* 3D texturing enabled, or texture border - fallback */
        FALLBACK (ctx, SAVAGE_FALLBACK_TEXTURE, GL_TRUE);
        return;
     }
@@ -1553,53 +1621,64 @@ static void savageUpdateTexState_s3d( 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;
+    format = tObj->Image[0][tObj->BaseLevel]->_BaseFormat;
 
     /* FIXME: copied from utah-glx, probably needs some tuning */
     switch (ctx->Texture.Unit[0].EnvMode) {
     case GL_DECAL:
-       imesa->regs.s3d.drawCtrl.ni.texBlendCtrl = SAVAGETBC_DECAL_S3D;
+       imesa->regs.s3d.drawCtrl.ni.texBlendCtrl = SAVAGETBC_DECALALPHA_S3D;
        break;
     case GL_REPLACE:
-       imesa->regs.s3d.drawCtrl.ni.texBlendCtrl = SAVAGETBC_COPY_S3D;
+       switch (format) {
+       case GL_ALPHA: /* FIXME */
+           imesa->regs.s3d.drawCtrl.ni.texBlendCtrl = 1;
+           break;
+       case GL_LUMINANCE_ALPHA:
+       case GL_RGBA:
+           imesa->regs.s3d.drawCtrl.ni.texBlendCtrl = 4;
+           break;
+       case GL_RGB:
+       case GL_LUMINANCE:
+           imesa->regs.s3d.drawCtrl.ni.texBlendCtrl = SAVAGETBC_DECAL_S3D;
+           break;
+       case GL_INTENSITY:
+           imesa->regs.s3d.drawCtrl.ni.texBlendCtrl = SAVAGETBC_COPY_S3D;
+       }
        break;
-    case GL_BLEND: /* FIXIT */
+    case GL_BLEND: /* hardware can't do GL_BLEND */
+       FALLBACK (ctx, SAVAGE_FALLBACK_TEXTURE, GL_TRUE);
+       return;
     case GL_MODULATE:
        imesa->regs.s3d.drawCtrl.ni.texBlendCtrl = SAVAGETBC_MODULATEALPHA_S3D;
        break;
     default:
-       fprintf(stderr, "unkown tex env mode\n");
+       fprintf(stderr, "unknown tex env mode\n");
        /*exit(1);*/
        break;                  
     }
 
-    imesa->regs.s3d.drawCtrl.ni.flushPdDestWrites = GL_TRUE;
-    imesa->regs.s3d.drawCtrl.ni.flushPdZbufWrites = GL_TRUE;
-
-    /* FIXME: this is how the utah-driver works. I doubt it's the ultimate 
-       truth. */
+    /* The Savage3D can't handle different wrapping modes in s and t.
+     * If they are not the same, fall back to software. */
+    if (t->setup.sWrapMode != t->setup.tWrapMode) {
+       FALLBACK (ctx, SAVAGE_FALLBACK_TEXTURE, GL_TRUE);
+       return;
+    }
     imesa->regs.s3d.texCtrl.ni.uWrapEn = 0;
     imesa->regs.s3d.texCtrl.ni.vWrapEn = 0;
-    if (t->texParams.sWrapMode == GL_CLAMP)
-       imesa->regs.s3d.texCtrl.ni.wrapMode = TAM_Clamp;
-    else
-       imesa->regs.s3d.texCtrl.ni.wrapMode = TAM_Wrap;
+    imesa->regs.s3d.texCtrl.ni.wrapMode =
+       (t->setup.sWrapMode == GL_REPEAT) ? TAM_Wrap : TAM_Clamp;
 
-    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;
@@ -1639,74 +1718,101 @@ static void savageUpdateTexState_s3d( GLcontext *ctx )
     }
     */
 
-    /* LOD bias makes corruption of small mipmap levels worse on Savage IX
-     * but doesn't show the desired effect with the lodbias mesa demo. */
-    imesa->regs.s3d.texCtrl.ni.dBias = 0;
+    if((ctx->Texture.Unit[0].LodBias !=0.0F) ||
+       (imesa->regs.s3d.texCtrl.ni.dBias != 0))
+    {
+       int bias = (int)(ctx->Texture.Unit[0].LodBias * 16.0);
+       if (bias < -256)
+           bias = -256;
+       else if (bias > 255)
+           bias = 255;
+       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.texAddr.ni.addr = (GLuint) t->texParams.hwPhysAddress >> 3;
-    if(t->heap == SAVAGE_AGP_HEAP) {
-       imesa->regs.s3d.texAddr.ni.inSysTex = 1;
-       imesa->regs.s3d.texAddr.ni.inAGPTex = 1;
-    } else {
-       imesa->regs.s3d.texAddr.ni.inSysTex = 0;
-       imesa->regs.s3d.texAddr.ni.inAGPTex = 1;
-    }
+    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->setup.physAddr | 2;
+    if(t->base.heap->heapId == SAVAGE_AGP_HEAP)
+       imesa->regs.s3d.texAddr.ui |= 0x1;
 }
 
 
+static void savageTimestampTextures( savageContextPtr imesa )
+{
+   /* Timestamp current texture objects for texture heap aging.
+    * Only useful with long-lived 32-bit event tags available
+    * with Savage DRM 2.3.x or later. */
+   if ((imesa->CurrentTexObj[0] || imesa->CurrentTexObj[1]) &&
+       imesa->savageScreen->driScrnPriv->drmMinor >= 3) {
+       unsigned int e;
+       FLUSH_BATCH(imesa);
+       e = savageEmitEvent(imesa, SAVAGE_WAIT_3D);
+       if (imesa->CurrentTexObj[0])
+          imesa->CurrentTexObj[0]->timestamp = e;
+       if (imesa->CurrentTexObj[1])
+          imesa->CurrentTexObj[1]->timestamp = e;
+   }
+}
+
 
 static void savageUpdateTextureState_s4( GLcontext *ctx )
 {
    savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
+
+   /* When a texture is about to change or be disabled, timestamp the
+    * old texture(s). We'll have to wait for this time stamp before
+    * uploading anything to the same texture heap.
+    */
+   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))
+       savageTimestampTextures(imesa);
+
    if (imesa->CurrentTexObj[0]) imesa->CurrentTexObj[0]->bound &= ~1;
    if (imesa->CurrentTexObj[1]) imesa->CurrentTexObj[1]->bound &= ~2;
    imesa->CurrentTexObj[0] = 0;
    imesa->CurrentTexObj[1] = 0;   
-   FALLBACK (ctx, SAVAGE_FALLBACK_TEXTURE, GL_FALSE);
    savageUpdateTex0State_s4( ctx );
    savageUpdateTex1State_s4( ctx );
-   imesa->dirty |= (SAVAGE_UPLOAD_CTX |
-                   SAVAGE_UPLOAD_TEX0 | 
+   imesa->dirty |= (SAVAGE_UPLOAD_TEX0 | 
                    SAVAGE_UPLOAD_TEX1);
 }
 static void savageUpdateTextureState_s3d( GLcontext *ctx )
 {
     savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
+
+   /* When a texture is about to change or be disabled, timestamp the
+    * old texture(s). We'll have to wait for this time stamp before
+    * uploading anything to the same texture heap.
+    */
+    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))
+       savageTimestampTextures(imesa);
+
     if (imesa->CurrentTexObj[0]) imesa->CurrentTexObj[0]->bound &= ~1;
     imesa->CurrentTexObj[0] = 0;
-    if (ctx->Texture.Unit[1]._ReallyEnabled) {
-       FALLBACK (ctx, SAVAGE_FALLBACK_TEXTURE, GL_TRUE);
-    } else {
-       FALLBACK (ctx, SAVAGE_FALLBACK_TEXTURE, GL_FALSE);
-       savageUpdateTexState_s3d( ctx );
-       imesa->dirty |= (SAVAGE_UPLOAD_CTX |
-                        SAVAGE_UPLOAD_TEX0);
-    }
+    savageUpdateTexState_s3d( ctx );
+    imesa->dirty |= (SAVAGE_UPLOAD_TEX0);
 }
-static void savageUpdateTextureState_first( GLcontext *ctx)
+void savageUpdateTextureState( GLcontext *ctx)
 {
     savageContextPtr imesa = SAVAGE_CONTEXT( ctx );
-    if (imesa->savageScreen->chipset <= S3_SAVAGE4) {
-       GetTiledCoordinates16 = GetTiledCoordinates16_4;
-       GetTiledCoordinates32 = GetTiledCoordinates32_4;
-    } else {
-       GetTiledCoordinates16 = GetTiledCoordinates16_8;
-       GetTiledCoordinates32 = GetTiledCoordinates32_8;
-    }
+    FALLBACK (ctx, SAVAGE_FALLBACK_TEXTURE, GL_FALSE);
+    FALLBACK(ctx, SAVAGE_FALLBACK_PROJ_TEXTURE, GL_FALSE);
     if (imesa->savageScreen->chipset >= S3_SAVAGE4)
-       savageUpdateTextureState = savageUpdateTextureState_s4;
+       savageUpdateTextureState_s4 (ctx);
     else
-       savageUpdateTextureState = savageUpdateTextureState_s3d;
-    savageUpdateTextureState (ctx);
+       savageUpdateTextureState_s3d (ctx);
 }
-void (*savageUpdateTextureState)( GLcontext *ctx ) =
-    savageUpdateTextureState_first;
 
 
 
@@ -1728,21 +1834,90 @@ static void savageTexEnv( GLcontext *ctx, GLenum target,
       struct gl_texture_unit *texUnit = 
         &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
       const GLfloat *fc = texUnit->EnvColor;
-      GLuint r, g, b, a, col;
+      GLuint r, g, b, a;
       CLAMPED_FLOAT_TO_UBYTE(r, fc[0]);
       CLAMPED_FLOAT_TO_UBYTE(g, fc[1]);
       CLAMPED_FLOAT_TO_UBYTE(b, fc[2]);
       CLAMPED_FLOAT_TO_UBYTE(a, fc[3]);
 
-      col = ((a << 24) | 
-            (r << 16) | 
-            (g <<  8) | 
-            (b <<  0));
+      imesa->texEnvColor = ((a << 24) | (r << 16) | 
+                           (g <<  8) | (b <<  0));
     
 
    } 
 }
 
+/* Update the heap's time stamp, so the new image is not uploaded
+ * while the old one is still in use. If the texture that is going to
+ * be changed is currently bound, we need to timestamp the texture
+ * first. */
+static void savageTexImageChanged (savageTexObjPtr t) {
+    if (t->base.heap) {
+       if (t->base.bound)
+           savageTimestampTextures(
+               (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,
+                             GLenum format, GLenum type, const GLvoid *pixels,
+                             const struct gl_pixelstore_attrib *packing,
+                             struct gl_texture_object *texObj,
+                             struct gl_texture_image *texImage )
+{
+   savageTexObjPtr t = (savageTexObjPtr) texObj->DriverData;
+   if (t) {
+      savageTexImageChanged (t);
+   } else {
+      t = savageAllocTexObj(texObj);
+      if (!t) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage1D");
+         return;
+      }
+   }
+   _mesa_store_teximage1d( ctx, target, level, internalFormat,
+                          width, border, format, type,
+                          pixels, packing, texObj, texImage );
+   t->base.dirty_images[0] |= (1 << level);
+   SAVAGE_CONTEXT(ctx)->new_state |= SAVAGE_NEW_TEXTURE;
+}
+
+static void savageTexSubImage1D( GLcontext *ctx, 
+                                GLenum target,
+                                GLint level,   
+                                GLint xoffset,
+                                GLsizei width,
+                                GLenum format, GLenum type,
+                                const GLvoid *pixels,
+                                const struct gl_pixelstore_attrib *packing,
+                                struct gl_texture_object *texObj,
+                                struct gl_texture_image *texImage )
+{
+   savageTexObjPtr t = (savageTexObjPtr) texObj->DriverData;
+   assert( t ); /* this _should_ be true */
+   if (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->dirtySubImages |= (1 << level);
+   SAVAGE_CONTEXT(ctx)->new_state |= SAVAGE_NEW_TEXTURE;
+}
+
 static void savageTexImage2D( GLcontext *ctx, GLenum target, GLint level,
                              GLint internalFormat,
                              GLint width, GLint height, GLint border,
@@ -1751,9 +1926,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) {
@@ -1764,7 +1939,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;
 }
 
@@ -1779,21 +1954,81 @@ 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, "glTexImage2D");
+         _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;
+}
+
+static void
+savageCompressedTexImage2D( GLcontext *ctx, GLenum target, GLint level,
+                           GLint internalFormat,
+                           GLint width, GLint height, GLint border,
+                           GLsizei imageSize, const GLvoid *data,
+                           struct gl_texture_object *texObj,
+                           struct gl_texture_image *texImage )
+{
+   savageTexObjPtr t = (savageTexObjPtr) texObj->DriverData;
+   if (t) {
+      savageTexImageChanged (t);
+   } else {
+      t = savageAllocTexObj(texObj);
+      if (!t) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexImage2D");
+         return;
+      }
+   }
+   _mesa_store_compressed_teximage2d( ctx, target, level, internalFormat,
+                                     width, height, border, imageSize,
+                                     data, texObj, texImage );
+   t->base.dirty_images[0] |= (1 << level);
+   SAVAGE_CONTEXT(ctx)->new_state |= SAVAGE_NEW_TEXTURE;
+}
+
+static void
+savageCompressedTexSubImage2D( GLcontext *ctx, 
+                              GLenum target,
+                              GLint level,     
+                              GLint xoffset, GLint yoffset,
+                              GLsizei width, GLsizei height,
+                              GLenum format, GLsizei imageSize,
+                              const GLvoid *data,
+                              struct gl_texture_object *texObj,
+                              struct gl_texture_image *texImage )
+{
+   savageTexObjPtr t = (savageTexObjPtr) texObj->DriverData;
+   assert( t ); /* this _should_ be true */
+   if (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_compressed_texsubimage2d(ctx, target, level, xoffset, yoffset,
+                                       width, height, format, imageSize,
+                                       data, texObj, texImage);
+   t->dirtySubImages |= (1 << level);
    SAVAGE_CONTEXT(ctx)->new_state |= SAVAGE_NEW_TEXTURE;
 }
 
@@ -1801,10 +2036,10 @@ 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_2D)
+   if (!t || (target != GL_TEXTURE_1D && target != GL_TEXTURE_2D))
       return;
 
    switch (pname) {
@@ -1834,44 +2069,28 @@ static void savageBindTexture( GLcontext *ctx, GLenum target,
 {
    savageContextPtr imesa = SAVAGE_CONTEXT( ctx );
    
-   assert( (target != GL_TEXTURE_2D) || (tObj->DriverData != NULL) );
+   assert( (target != GL_TEXTURE_1D && target != GL_TEXTURE_2D) ||
+          (tObj->DriverData != NULL) );
 
    imesa->new_state |= SAVAGE_NEW_TEXTURE;
 }
 
 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)
+        savageTimestampTextures(imesa);
 
-      if (t->bound) {
-        imesa->CurrentTexObj[t->bound-1] = 0;
-        imesa->new_state |= SAVAGE_NEW_TEXTURE;
-      }
-
-      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 )
 {
@@ -1886,11 +2105,31 @@ void savageDDInitTextureFuncs( struct dd_function_table *functions )
 {
    functions->TexEnv = savageTexEnv;
    functions->ChooseTextureFormat = savageChooseTextureFormat;
+   functions->TexImage1D = savageTexImage1D;
+   functions->TexSubImage1D = savageTexSubImage1D;
    functions->TexImage2D = savageTexImage2D;
    functions->TexSubImage2D = savageTexSubImage2D;
+   functions->CompressedTexImage2D = savageCompressedTexImage2D;
+   functions->CompressedTexSubImage2D = savageCompressedTexSubImage2D;
    functions->BindTexture = savageBindTexture;
    functions->NewTextureObject = savageNewTextureObject;
    functions->DeleteTexture = savageDeleteTexture;
-   functions->IsTextureResident = savageIsTextureResident;
+   functions->IsTextureResident = driIsTextureResident;
    functions->TexParameter = savageTexParameter;
+
+   /* Texel fetching with our custom texture formats works just like
+    * the standard argb formats. */
+   _savage_texformat_a1114444.FetchTexel1D = _mesa_texformat_argb4444.FetchTexel1D;
+   _savage_texformat_a1114444.FetchTexel2D = _mesa_texformat_argb4444.FetchTexel2D;
+   _savage_texformat_a1114444.FetchTexel3D = _mesa_texformat_argb4444.FetchTexel3D;
+   _savage_texformat_a1114444.FetchTexel1Df= _mesa_texformat_argb4444.FetchTexel1Df;
+   _savage_texformat_a1114444.FetchTexel2Df= _mesa_texformat_argb4444.FetchTexel2Df;
+   _savage_texformat_a1114444.FetchTexel3Df= _mesa_texformat_argb4444.FetchTexel3Df;
+
+   _savage_texformat_a1118888.FetchTexel1D = _mesa_texformat_argb8888.FetchTexel1D;
+   _savage_texformat_a1118888.FetchTexel2D = _mesa_texformat_argb8888.FetchTexel2D;
+   _savage_texformat_a1118888.FetchTexel3D = _mesa_texformat_argb8888.FetchTexel3D;
+   _savage_texformat_a1118888.FetchTexel1Df= _mesa_texformat_argb8888.FetchTexel1Df;
+   _savage_texformat_a1118888.FetchTexel2Df= _mesa_texformat_argb8888.FetchTexel2Df;
+   _savage_texformat_a1118888.FetchTexel3Df= _mesa_texformat_argb8888.FetchTexel3Df;
 }