implement arb_vertex_program in hw for r200. Code contains still some hacks, generic...
[mesa.git] / src / mesa / drivers / glide / fxsetup.c
index 9ebc603ae7ae17b45a87467d97d873494b1cbbcf..240e5e0b5970dc2e77d0dcb8d56a15c2a45398fe 100644 (file)
@@ -1,5 +1,3 @@
-/* $Id: fxsetup.c,v 1.40 2003/10/02 17:36:44 brianp Exp $ */
-
 /*
  * Mesa 3-D graphics library
  * Version:  4.0
 
 #include "fxdrv.h"
 #include "enums.h"
+#include "tnl.h"
 #include "tnl/t_context.h"
+#include "swrast.h"
+#include "texstore.h"
+
 
 static void
 fxTexValidate(GLcontext * ctx, struct gl_texture_object *tObj)
@@ -54,69 +56,106 @@ fxTexValidate(GLcontext * ctx, struct gl_texture_object *tObj)
 
    if (ti->validated) {
       if (TDFX_DEBUG & VERBOSE_DRIVER) {
-        fprintf(stderr, "%s: validated=GL_TRUE\n", __FUNCTION__);
+        fprintf(stderr, "fxTexValidate(NOP)\n");
       }
       return;
    }
 
    if (TDFX_DEBUG & VERBOSE_DRIVER) {
-      fprintf(stderr, "%s(%p (%d))\n", __FUNCTION__, (void *)tObj, tObj->Name);
+      fprintf(stderr, "fxTexValidate(%p (%d))\n", (void *)tObj, tObj->Name);
    }
 
    ti->tObj = tObj;
    minl = ti->minLevel = tObj->BaseLevel;
-   maxl = ti->maxLevel = MIN2(tObj->MaxLevel, tObj->Image[0]->MaxLog2);
+   maxl = ti->maxLevel = MIN2(tObj->MaxLevel, tObj->Image[0][0]->MaxLog2);
+
+#if FX_RESCALE_BIG_TEXURES_HACK
+{
+   fxMesaContext fxMesa = FX_CONTEXT(ctx);
+   /* [dBorca]
+    * Fake textures larger than HW supports:
+    * 1) we have mipmaps. Then we just push up to the first supported
+    *    LOD. A possible drawback is that Mesa will ignore the skipped
+    *    LODs on further texture handling.
+    *    Will this interfere with GL_TEXTURE_[MIN|BASE]_LEVEL? How?
+    * 2) we don't have mipmaps. We need to rescale the big LOD in place.
+    *    The above approach is somehow dumb! we might have rescaled
+    *    once in TexImage2D to accomodate aspect ratio, and now we
+    *    are rescaling again. The thing is, in TexImage2D we don't
+    *    know whether we'll hit 1) or 2) by the time of validation.
+    */
+   if ((tObj->MinFilter == GL_NEAREST) || (tObj->MinFilter == GL_LINEAR)) {
+      /* no mipmaps! */
+      struct gl_texture_image *texImage = tObj->Image[0][minl];
+      tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
+      GLint _w, _h, maxSize = 1 << fxMesa->textureMaxLod;
+      if ((mml->width > maxSize) || (mml->height > maxSize)) {
+         /* need to rescale */
+         GLint texelBytes = texImage->TexFormat->TexelBytes;
+         GLvoid *texImage_Data = texImage->Data;
+         _w = MIN2(texImage->Width, maxSize);
+         _h = MIN2(texImage->Height, maxSize);
+         if (TDFX_DEBUG & VERBOSE_TEXTURE) {
+            fprintf(stderr, "fxTexValidate: rescaling %d x %d -> %d x %d\n",
+                            texImage->Width, texImage->Height, _w, _h);
+         }
+         /* we should leave these as is and... (!) */
+         texImage->Width = _w;
+         texImage->Height = _h;
+         fxTexGetInfo(_w, _h, NULL, NULL, NULL, NULL,
+                      &(mml->wScale), &(mml->hScale));
+         _w *= mml->wScale;
+         _h *= mml->hScale;
+         texImage->Data = _mesa_malloc(_w * _h * texelBytes);
+         _mesa_rescale_teximage2d(texelBytes,
+                                  mml->width,
+                                  _w * texelBytes, /* dst stride */
+                                  mml->width, mml->height, /* src */
+                                  _w, _h, /* dst */
+                                  texImage_Data /*src*/, texImage->Data /*dst*/ );
+         _mesa_free(texImage_Data);
+         mml->width = _w;
+         mml->height = _h;
+         /* (!) ... and set mml->wScale = _w / texImage->Width */
+      }
+   } else {
+      /* mipmapping */
+      if (maxl - minl > fxMesa->textureMaxLod) {
+         /* skip a certain number of LODs */
+         minl += maxl - fxMesa->textureMaxLod;
+         if (TDFX_DEBUG & VERBOSE_TEXTURE) {
+            fprintf(stderr, "fxTexValidate: skipping %d LODs\n", minl - ti->minLevel);
+         }
+         ti->minLevel = tObj->BaseLevel = minl;
+      }
+   }
+}
+#endif
 
-   fxTexGetInfo(tObj->Image[minl]->Width, tObj->Image[minl]->Height,
+   fxTexGetInfo(tObj->Image[0][minl]->Width, tObj->Image[0][minl]->Height,
                &(FX_largeLodLog2(ti->info)), &(FX_aspectRatioLog2(ti->info)),
                &(ti->sScale), &(ti->tScale),
                NULL, NULL);
 
    if ((tObj->MinFilter != GL_NEAREST) && (tObj->MinFilter != GL_LINEAR))
-      fxTexGetInfo(tObj->Image[maxl]->Width, tObj->Image[maxl]->Height,
+      fxTexGetInfo(tObj->Image[0][maxl]->Width, tObj->Image[0][maxl]->Height,
                   &(FX_smallLodLog2(ti->info)), NULL,
                   NULL, NULL, NULL, NULL);
    else
       FX_smallLodLog2(ti->info) = FX_largeLodLog2(ti->info);
 
-/*jejeje*/
-ti->baseLevelInternalFormat = tObj->Image[minl]->Format;
-#if 0
-   fxTexGetFormat(ctx, tObj->Image[minl]->TexFormat->BaseFormat, &(ti->info.format),
-                 &(ti->baseLevelInternalFormat)); /* [koolsmoky] */
-#endif
-
-   switch (tObj->WrapS) {
-   case GL_MIRRORED_REPEAT:
-      ti->sClamp = GR_TEXTURECLAMP_MIRROR_EXT;
-      break;
-   case GL_CLAMP_TO_EDGE:
-      /* What's this really mean compared to GL_CLAMP? */
-   case GL_CLAMP:
-      ti->sClamp = GR_TEXTURECLAMP_CLAMP;
-      break;
-   case GL_REPEAT:
-      ti->sClamp = GR_TEXTURECLAMP_WRAP;
-      break;
-   default:
-      ;                                /* silence compiler warning */
-   }
-   switch (tObj->WrapT) {
-   case GL_MIRRORED_REPEAT:
-      ti->tClamp = GR_TEXTURECLAMP_MIRROR_EXT;
-      break;
-   case GL_CLAMP_TO_EDGE:
-      /* What's this really mean compared to GL_CLAMP? */
-   case GL_CLAMP:
-      ti->tClamp = GR_TEXTURECLAMP_CLAMP;
-      break;
-   case GL_REPEAT:
-      ti->tClamp = GR_TEXTURECLAMP_WRAP;
-      break;
-   default:
-      ;                                /* silence compiler warning */
+   /* [dBorca] this is necessary because of fxDDCompressedTexImage2D */
+   if (ti->padded) {
+      struct gl_texture_image *texImage = tObj->Image[0][minl];
+      tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
+      if (mml->wScale != 1 || mml->hScale != 1) {
+         ti->sScale /= mml->wScale;
+         ti->tScale /= mml->hScale;
+      }
    }
 
+   ti->baseLevelInternalFormat = tObj->Image[0][minl]->Format;
+
    ti->validated = GL_TRUE;
 
    ti->info.data = NULL;
@@ -280,7 +319,7 @@ fxGetTexSetConfiguration(GLcontext * ctx,
    unitsmode |= (ifmt | envmode);
 
    if (TDFX_DEBUG & (VERBOSE_DRIVER | VERBOSE_TEXTURE))
-      fxPrintUnitsMode(__FUNCTION__, unitsmode);
+      fxPrintUnitsMode("fxGetTexSetConfiguration", unitsmode);
 
    return unitsmode;
 }
@@ -298,9 +337,11 @@ fxSetupSingleTMU_NoLock(fxMesaContext fxMesa, struct gl_texture_object *tObj)
    int tmu;
 
    if (TDFX_DEBUG & VERBOSE_DRIVER) {
-      fprintf(stderr, "%s(%p (%d))\n", __FUNCTION__, (void *)tObj, tObj->Name);
+      fprintf(stderr, "fxSetupSingleTMU_NoLock(%p (%d))\n", (void *)tObj, tObj->Name);
    }
 
+   ti->lastTimeUsed = fxMesa->texBindNumber;
+
    /* Make sure we're not loaded incorrectly */
    if (ti->isInTM) {
       if (ti->LODblend) {
@@ -318,13 +359,8 @@ fxSetupSingleTMU_NoLock(fxMesaContext fxMesa, struct gl_texture_object *tObj)
       if (ti->LODblend)
         fxTMMoveInTM_NoLock(fxMesa, tObj, FX_TMU_SPLIT);
       else {
-         /* XXX putting textures into the second memory bank when the
-          * first bank is full is not working at this time.
-          */
-        if (/*[dBorca]: fixme*/0 && fxMesa->haveTwoTMUs) {
-           if (fxMesa->freeTexMem[FX_TMU0] >
-               grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH,
-                                                 &(ti->info))) {
+        if (fxMesa->haveTwoTMUs) {
+            if (fxTMCheckStartAddr(fxMesa, FX_TMU0, ti)) {
               fxTMMoveInTM_NoLock(fxMesa, tObj, FX_TMU0);
            }
            else {
@@ -337,12 +373,13 @@ fxSetupSingleTMU_NoLock(fxMesaContext fxMesa, struct gl_texture_object *tObj)
    }
 
    if (ti->LODblend && ti->whichTMU == FX_TMU_SPLIT) {
+      /* broadcast */
       if ((ti->info.format == GR_TEXFMT_P_8)
          && (!fxMesa->haveGlobalPaletteTexture)) {
         if (TDFX_DEBUG & VERBOSE_DRIVER) {
-           fprintf(stderr, "%s: uploading texture palette\n", __FUNCTION__);
+           fprintf(stderr, "fxSetupSingleTMU_NoLock: uploading texture palette\n");
         }
-        grTexDownloadTable(GR_TEXTABLE_PALETTE, &(ti->palette));
+        grTexDownloadTable(ti->paltype, &(ti->palette));
       }
 
       grTexClampMode(GR_TMU0, ti->sClamp, ti->tClamp);
@@ -363,12 +400,13 @@ fxSetupSingleTMU_NoLock(fxMesaContext fxMesa, struct gl_texture_object *tObj)
       else
         tmu = ti->whichTMU;
 
+      /* pointcast */
       if ((ti->info.format == GR_TEXFMT_P_8)
          && (!fxMesa->haveGlobalPaletteTexture)) {
         if (TDFX_DEBUG & VERBOSE_DRIVER) {
-           fprintf(stderr, "%s: uploading texture palette\n", __FUNCTION__);
+           fprintf(stderr, "fxSetupSingleTMU_NoLock: uploading texture palette\n");
         }
-        grTexDownloadTable(GR_TEXTABLE_PALETTE, &(ti->palette));
+        fxMesa->Glide.grTexDownloadTableExt(tmu, ti->paltype, &(ti->palette));
       }
 
       /* KW: The alternative is to do the download to the other tmu.  If
@@ -376,7 +414,7 @@ fxSetupSingleTMU_NoLock(fxMesaContext fxMesa, struct gl_texture_object *tObj)
        * texture memory, so perhaps it's not a good idea.  
        */
       if (ti->LODblend && (TDFX_DEBUG & VERBOSE_DRIVER)) {
-        fprintf(stderr, "%s: not blending texture - only one tmu\n", __FUNCTION__);
+        fprintf(stderr, "fxSetupSingleTMU_NoLock: not blending texture - only one tmu\n");
       }
 
       grTexClampMode(tmu, ti->sClamp, ti->tClamp);
@@ -390,75 +428,92 @@ fxSetupSingleTMU_NoLock(fxMesaContext fxMesa, struct gl_texture_object *tObj)
 static void
 fxSelectSingleTMUSrc_NoLock(fxMesaContext fxMesa, GLint tmu, FxBool LODblend)
 {
+   struct tdfx_texcombine tex0, tex1;
+
    if (TDFX_DEBUG & VERBOSE_DRIVER) {
-      fprintf(stderr, "%s(%d, %d)\n", __FUNCTION__, tmu, LODblend);
+      fprintf(stderr, "fxSelectSingleTMUSrc_NoLock(%d, %d)\n", tmu, LODblend);
    }
 
+   tex0.InvertRGB     = FXFALSE;
+   tex0.InvertAlpha   = FXFALSE;
+   tex1.InvertRGB     = FXFALSE;
+   tex1.InvertAlpha   = FXFALSE;
+
    if (LODblend) {
-      grTexCombine(GR_TMU0,
-                            GR_COMBINE_FUNCTION_BLEND,
-                            GR_COMBINE_FACTOR_ONE_MINUS_LOD_FRACTION,
-                            GR_COMBINE_FUNCTION_BLEND,
-                            GR_COMBINE_FACTOR_ONE_MINUS_LOD_FRACTION,
-                            FXFALSE, FXFALSE);
-
-      if (fxMesa->haveTwoTMUs)
-        grTexCombine(GR_TMU1,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE);
+      tex0.FunctionRGB   = GR_COMBINE_FUNCTION_BLEND;
+      tex0.FactorRGB     = GR_COMBINE_FACTOR_ONE_MINUS_LOD_FRACTION;
+      tex0.FunctionAlpha = GR_COMBINE_FUNCTION_BLEND;
+      tex0.FactorAlpha   = GR_COMBINE_FACTOR_ONE_MINUS_LOD_FRACTION;
+
+      tex1.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+      tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+      tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+      tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+
       fxMesa->tmuSrc = FX_TMU_SPLIT;
    }
    else {
       if (tmu != FX_TMU1) {
-        grTexCombine(GR_TMU0,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE);
-        if (fxMesa->haveTwoTMUs) {
-           grTexCombine(GR_TMU1,
-                                  GR_COMBINE_FUNCTION_ZERO,
-                                  GR_COMBINE_FACTOR_NONE,
-                                  GR_COMBINE_FUNCTION_ZERO,
-                                  GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE);
-        }
+         tex0.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+         tex0.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+         tex0.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+         tex0.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+
+         tex1.FunctionRGB   = GR_COMBINE_FUNCTION_ZERO;
+         tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+         tex1.FunctionAlpha = GR_COMBINE_FUNCTION_ZERO;
+         tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+
         fxMesa->tmuSrc = FX_TMU0;
       }
       else {
-        grTexCombine(GR_TMU1,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE);
+         tex1.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+         tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+         tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+         tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
 
-        /* GR_COMBINE_FUNCTION_SCALE_OTHER doesn't work ?!? */
-
-        grTexCombine(GR_TMU0,
-                               GR_COMBINE_FUNCTION_BLEND,
-                               GR_COMBINE_FACTOR_ONE,
-                               GR_COMBINE_FUNCTION_BLEND,
-                               GR_COMBINE_FACTOR_ONE, FXFALSE, FXFALSE);
+        /* correct values to set TMU0 in passthrough mode */
+         tex0.FunctionRGB   = GR_COMBINE_FUNCTION_BLEND;
+         tex0.FactorRGB     = GR_COMBINE_FACTOR_ONE;
+         tex0.FunctionAlpha = GR_COMBINE_FUNCTION_BLEND;
+         tex0.FactorAlpha   = GR_COMBINE_FACTOR_ONE;
 
         fxMesa->tmuSrc = FX_TMU1;
       }
    }
+
+   grTexCombine(GR_TMU0,
+                tex0.FunctionRGB,
+                tex0.FactorRGB,
+                tex0.FunctionAlpha,
+                tex0.FactorAlpha,
+                tex0.InvertRGB,
+                tex0.InvertAlpha);
+   if (fxMesa->haveTwoTMUs) {
+      grTexCombine(GR_TMU1,
+                   tex1.FunctionRGB,
+                   tex1.FactorRGB,
+                   tex1.FunctionAlpha,
+                   tex1.FactorAlpha,
+                   tex1.InvertRGB,
+                   tex1.InvertAlpha);
+   }
 }
 
 static void
 fxSetupTextureSingleTMU_NoLock(GLcontext * ctx, GLuint textureset)
 {
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
+   struct tdfx_combine alphaComb, colorComb;
    GrCombineLocal_t localc, locala;
    GLuint unitsmode;
    GLint ifmt;
    tfxTexInfo *ti;
-   struct gl_texture_object *tObj = ctx->Texture.Unit[textureset].Current2D;
+   struct gl_texture_object *tObj = ctx->Texture.Unit[textureset]._Current;
    int tmu;
 
    if (TDFX_DEBUG & VERBOSE_DRIVER) {
-      fprintf(stderr, "%s(...)\n", __FUNCTION__);
+      fprintf(stderr, "fxSetupTextureSingleTMU_NoLock(%d)\n", textureset);
    }
 
    ti = fxTMGetTexInfo(tObj);
@@ -500,62 +555,166 @@ fxSetupTextureSingleTMU_NoLock(GLcontext * ctx, GLuint textureset)
       localc = GR_COMBINE_LOCAL_CONSTANT;
 
    if (TDFX_DEBUG & (VERBOSE_DRIVER | VERBOSE_TEXTURE))
-      fprintf(stderr, "%s: envmode is %s\n", __FUNCTION__,
+      fprintf(stderr, "fxSetupTextureSingleTMU_NoLock: envmode is %s\n",
              _mesa_lookup_enum_by_nr(ctx->Texture.Unit[textureset].EnvMode));
 
+   alphaComb.Local    = locala;
+   alphaComb.Invert   = FXFALSE;
+   colorComb.Local    = localc;
+   colorComb.Invert   = FXFALSE;
+
    switch (ctx->Texture.Unit[textureset].EnvMode) {
    case GL_DECAL:
-      grAlphaCombine(GR_COMBINE_FUNCTION_LOCAL,
-                              GR_COMBINE_FACTOR_NONE,
-                              locala, GR_COMBINE_OTHER_NONE, FXFALSE);
+      alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+      alphaComb.Factor   = GR_COMBINE_FACTOR_NONE;
+      alphaComb.Other    = GR_COMBINE_OTHER_NONE;
 
-      grColorCombine(GR_COMBINE_FUNCTION_BLEND,
-                              GR_COMBINE_FACTOR_TEXTURE_ALPHA,
-                              localc, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
+      colorComb.Function = GR_COMBINE_FUNCTION_BLEND;
+      colorComb.Factor   = GR_COMBINE_FACTOR_TEXTURE_ALPHA;
+      colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
       break;
    case GL_MODULATE:
-      grAlphaCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                              GR_COMBINE_FACTOR_LOCAL,
-                              locala, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
-
-      if (ifmt == GL_ALPHA)
-        grColorCombine(GR_COMBINE_FUNCTION_LOCAL,
-                                 GR_COMBINE_FACTOR_NONE,
-                                 localc, GR_COMBINE_OTHER_NONE, FXFALSE);
-      else
-        grColorCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                                 GR_COMBINE_FACTOR_LOCAL,
-                                 localc, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
+      alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+      alphaComb.Factor   = GR_COMBINE_FACTOR_LOCAL;
+      alphaComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+
+      if (ifmt == GL_ALPHA) {
+         colorComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+         colorComb.Factor   = GR_COMBINE_FACTOR_NONE;
+         colorComb.Other    = GR_COMBINE_OTHER_NONE;
+      } else {
+         colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         colorComb.Factor   = GR_COMBINE_FACTOR_LOCAL;
+         colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+      }
       break;
    case GL_BLEND:
-      if (TDFX_DEBUG & VERBOSE_DRIVER)
-        fprintf(stderr, "%s: GL_BLEND not yet supported\n", __FUNCTION__);
+      if (ifmt == GL_LUMINANCE || ifmt == GL_RGB) {
+         /* Av = Af */
+         alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_NONE;
+         alphaComb.Other    = GR_COMBINE_OTHER_NONE;
+      }
+      else if (ifmt == GL_INTENSITY) {
+         /* Av = Af * (1 - It) + Ac * It */
+         alphaComb.Function = GR_COMBINE_FUNCTION_BLEND;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_TEXTURE_ALPHA;
+         alphaComb.Other    = GR_COMBINE_OTHER_CONSTANT;
+      }
+      else {
+         /* Av = Af * At */
+         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_LOCAL;
+         alphaComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+      }
+
+      if (ifmt == GL_ALPHA) {
+         colorComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+         colorComb.Factor   = GR_COMBINE_FACTOR_NONE;
+         colorComb.Other    = GR_COMBINE_OTHER_NONE;
+      } else {
+         if (fxMesa->type >= GR_SSTTYPE_Voodoo2) {
+            colorComb.Function = GR_COMBINE_FUNCTION_BLEND;
+            colorComb.Factor   = GR_COMBINE_FACTOR_TEXTURE_RGB;
+            colorComb.Other    = GR_COMBINE_OTHER_CONSTANT;
+         } else if (ifmt == GL_INTENSITY) {
+            /* just a hack: RGB == ALPHA */
+            colorComb.Function = GR_COMBINE_FUNCTION_BLEND;
+            colorComb.Factor   = GR_COMBINE_FACTOR_TEXTURE_ALPHA;
+            colorComb.Other    = GR_COMBINE_OTHER_CONSTANT;
+         } else {
+            /* Only Voodoo^2 can GL_BLEND (GR_COMBINE_FACTOR_TEXTURE_RGB)
+             * These settings assume that the TexEnv color is black and
+             * incoming fragment color is white.
+             */
+            colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+            colorComb.Factor   = GR_COMBINE_FACTOR_ONE;
+            colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+            colorComb.Invert   = FXTRUE;
+            _mesa_problem(NULL, "can't GL_BLEND with SST1");
+         }
+      }
+
+      grConstantColorValue(
+         (((GLuint)(ctx->Texture.Unit[textureset].EnvColor[0] * 255.0f))      ) |
+         (((GLuint)(ctx->Texture.Unit[textureset].EnvColor[1] * 255.0f)) <<  8) |
+         (((GLuint)(ctx->Texture.Unit[textureset].EnvColor[2] * 255.0f)) << 16) |
+         (((GLuint)(ctx->Texture.Unit[textureset].EnvColor[3] * 255.0f)) << 24));
       break;
    case GL_REPLACE:
-      if ((ifmt == GL_RGB) || (ifmt == GL_LUMINANCE))
-        grAlphaCombine(GR_COMBINE_FUNCTION_LOCAL,
-                                 GR_COMBINE_FACTOR_NONE,
-                                 locala, GR_COMBINE_OTHER_NONE, FXFALSE);
-      else
-        grAlphaCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                                 GR_COMBINE_FACTOR_ONE,
-                                 locala, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
-
-      if (ifmt == GL_ALPHA)
-        grColorCombine(GR_COMBINE_FUNCTION_LOCAL,
-                                 GR_COMBINE_FACTOR_NONE,
-                                 localc, GR_COMBINE_OTHER_NONE, FXFALSE);
-      else
-        grColorCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                                 GR_COMBINE_FACTOR_ONE,
-                                 localc, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
+      if ((ifmt == GL_RGB) || (ifmt == GL_LUMINANCE)) {
+         alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_NONE;
+         alphaComb.Other    = GR_COMBINE_OTHER_NONE;
+      } else {
+         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_ONE;
+         alphaComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+      }
+
+      if (ifmt == GL_ALPHA) {
+         colorComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+         colorComb.Factor   = GR_COMBINE_FACTOR_NONE;
+         colorComb.Other    = GR_COMBINE_OTHER_NONE;
+      } else {
+         colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         colorComb.Factor   = GR_COMBINE_FACTOR_ONE;
+         colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+      }
+      break;
+   case GL_ADD:
+      if (ifmt == GL_ALPHA ||
+          ifmt == GL_LUMINANCE_ALPHA ||
+          ifmt == GL_RGBA) {
+         /* product of texel and fragment alpha */
+         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_LOCAL;
+         alphaComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+      }
+      else if (ifmt == GL_LUMINANCE || ifmt == GL_RGB) {
+         /* fragment alpha is unchanged */
+         alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_NONE;
+         alphaComb.Other    = GR_COMBINE_OTHER_NONE;
+      }
+      else {
+         /* sum of texel and fragment alpha */
+         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_ONE;
+         alphaComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+      }
+
+      if (ifmt == GL_ALPHA) {
+         /* rgb unchanged */
+         colorComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+         colorComb.Factor   = GR_COMBINE_FACTOR_NONE;
+         colorComb.Other    = GR_COMBINE_OTHER_NONE;
+      }
+      else {
+         /* sum of texel and fragment rgb */
+         colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
+         colorComb.Factor   = GR_COMBINE_FACTOR_ONE;
+         colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+      }
       break;
    default:
-      if (TDFX_DEBUG & VERBOSE_DRIVER)
-        fprintf(stderr, "%s: %x Texture.EnvMode not yet supported\n", __FUNCTION__,
+      if (TDFX_DEBUG & VERBOSE_DRIVER) {
+        fprintf(stderr, "fxSetupTextureSingleTMU_NoLock: %x Texture.EnvMode not yet supported\n",
                 ctx->Texture.Unit[textureset].EnvMode);
-      break;
+      }
+      return;
    }
+
+   grAlphaCombine(alphaComb.Function,
+                  alphaComb.Factor,
+                  alphaComb.Local,
+                  alphaComb.Other,
+                  alphaComb.Invert);
+   grColorCombine(colorComb.Function,
+                  colorComb.Factor,
+                  colorComb.Local,
+                  colorComb.Other,
+                  colorComb.Invert);
 }
 
 #if 00
@@ -589,7 +748,7 @@ fxSetupDoubleTMU_NoLock(fxMesaContext fxMesa,
    int tmu0 = 0, tmu1 = 1;
 
    if (TDFX_DEBUG & VERBOSE_DRIVER) {
-      fprintf(stderr, "%s(...)\n", __FUNCTION__);
+      fprintf(stderr, "fxSetupDoubleTMU_NoLock(...)\n");
    }
 
    /* We shouldn't need to do this. There is something wrong with
@@ -678,16 +837,23 @@ fxSetupDoubleTMU_NoLock(fxMesaContext fxMesa,
       }
    }
 
+   /* [dBorca] Hack alert:
+    * we put these in reverse order, so that if we can't
+    * do _REAL_ pointcast, the TMU0 table gets broadcasted
+    */
    if (!fxMesa->haveGlobalPaletteTexture) {
-      /* [dBorca]
-       * all TMUs share the same table.
-       * The next test shouldn't be TMU specific...
-       */
+      /* pointcast */
+      if (ti1->info.format == GR_TEXFMT_P_8) {
+        if (TDFX_DEBUG & VERBOSE_DRIVER) {
+           fprintf(stderr, "fxSetupDoubleTMU_NoLock: uploading texture palette for TMU1\n");
+        }
+        fxMesa->Glide.grTexDownloadTableExt(ti1->whichTMU, ti1->paltype, &(ti1->palette));
+      }
       if (ti0->info.format == GR_TEXFMT_P_8) {
         if (TDFX_DEBUG & VERBOSE_DRIVER) {
-           fprintf(stderr, "%s: uploading texture palette TMU0\n", __FUNCTION__);
+           fprintf(stderr, "fxSetupDoubleTMU_NoLock: uploading texture palette for TMU0\n");
         }
-        grTexDownloadTable(GR_TEXTABLE_PALETTE, &(ti0->palette));
+        fxMesa->Glide.grTexDownloadTableExt(ti0->whichTMU, ti0->paltype, &(ti0->palette));
       }
    }
 
@@ -715,15 +881,17 @@ static void
 fxSetupTextureDoubleTMU_NoLock(GLcontext * ctx)
 {
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
+   struct tdfx_combine alphaComb, colorComb;
+   struct tdfx_texcombine tex0, tex1;
    GrCombineLocal_t localc, locala;
    tfxTexInfo *ti0, *ti1;
-   struct gl_texture_object *tObj0 = ctx->Texture.Unit[0].Current2D;
-   struct gl_texture_object *tObj1 = ctx->Texture.Unit[1].Current2D;
+   struct gl_texture_object *tObj0 = ctx->Texture.Unit[1]._Current;
+   struct gl_texture_object *tObj1 = ctx->Texture.Unit[0]._Current;
    GLuint envmode, ifmt, unitsmode;
    int tmu0 = 0, tmu1 = 1;
 
    if (TDFX_DEBUG & VERBOSE_DRIVER) {
-      fprintf(stderr, "%s(...)\n", __FUNCTION__);
+      fprintf(stderr, "fxSetupTextureDoubleTMU_NoLock(...)\n");
    }
 
    ti0 = fxTMGetTexInfo(tObj0);
@@ -759,7 +927,7 @@ fxSetupTextureDoubleTMU_NoLock(GLcontext * ctx)
 
 
    if (TDFX_DEBUG & (VERBOSE_DRIVER | VERBOSE_TEXTURE))
-      fprintf(stderr, "%s: envmode is %s/%s\n", __FUNCTION__,
+      fprintf(stderr, "fxSetupTextureDoubleTMU_NoLock: envmode is %s/%s\n",
              _mesa_lookup_enum_by_nr(ctx->Texture.Unit[0].EnvMode),
              _mesa_lookup_enum_by_nr(ctx->Texture.Unit[1].EnvMode));
 
@@ -769,6 +937,16 @@ fxSetupTextureDoubleTMU_NoLock(GLcontext * ctx)
       tmu1 = 0;
    }
    fxMesa->tmuSrc = FX_TMU_BOTH;
+
+   tex0.InvertRGB     = FXFALSE;
+   tex0.InvertAlpha   = FXFALSE;
+   tex1.InvertRGB     = FXFALSE;
+   tex1.InvertAlpha   = FXFALSE;
+   alphaComb.Local    = locala;
+   alphaComb.Invert   = FXFALSE;
+   colorComb.Local    = localc;
+   colorComb.Invert   = FXFALSE;
+
    switch (envmode) {
    case (FX_UM_E0_MODULATE | FX_UM_E1_MODULATE):
       {
@@ -777,120 +955,111 @@ fxSetupTextureDoubleTMU_NoLock(GLcontext * ctx)
         isalpha[tmu0] = (ti0->baseLevelInternalFormat == GL_ALPHA);
         isalpha[tmu1] = (ti1->baseLevelInternalFormat == GL_ALPHA);
 
-        if (isalpha[FX_TMU1])
-           grTexCombine(GR_TMU1,
-                                  GR_COMBINE_FUNCTION_ZERO,
-                                  GR_COMBINE_FACTOR_NONE,
-                                  GR_COMBINE_FUNCTION_LOCAL,
-                                  GR_COMBINE_FACTOR_NONE, FXTRUE, FXFALSE);
-        else
-           grTexCombine(GR_TMU1,
-                                  GR_COMBINE_FUNCTION_LOCAL,
-                                  GR_COMBINE_FACTOR_NONE,
-                                  GR_COMBINE_FUNCTION_LOCAL,
-                                  GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE);
-
-        if (isalpha[FX_TMU0])
-           grTexCombine(GR_TMU0,
-                                  GR_COMBINE_FUNCTION_BLEND_OTHER,
-                                  GR_COMBINE_FACTOR_ONE,
-                                  GR_COMBINE_FUNCTION_BLEND_OTHER,
-                                  GR_COMBINE_FACTOR_LOCAL, FXFALSE, FXFALSE);
-        else
-           grTexCombine(GR_TMU0,
-                                  GR_COMBINE_FUNCTION_BLEND_OTHER,
-                                  GR_COMBINE_FACTOR_LOCAL,
-                                  GR_COMBINE_FUNCTION_BLEND_OTHER,
-                                  GR_COMBINE_FACTOR_LOCAL, FXFALSE, FXFALSE);
-
-        grColorCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                                 GR_COMBINE_FACTOR_LOCAL,
-                                 localc, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
-
-        grAlphaCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                                 GR_COMBINE_FACTOR_LOCAL,
-                                 locala, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
+        if (isalpha[FX_TMU1]) {
+            tex1.FunctionRGB   = GR_COMBINE_FUNCTION_ZERO;
+            tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+            tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+            tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+            tex1.InvertRGB     = FXTRUE;
+        } else {
+            tex1.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+            tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+            tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+            tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+         }
+
+        if (isalpha[FX_TMU0]) {
+            tex0.FunctionRGB   = GR_COMBINE_FUNCTION_BLEND_OTHER;
+            tex0.FactorRGB     = GR_COMBINE_FACTOR_ONE;
+            tex0.FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
+            tex0.FactorAlpha   = GR_COMBINE_FACTOR_LOCAL;
+        } else {
+            tex0.FunctionRGB   = GR_COMBINE_FUNCTION_BLEND_OTHER;
+            tex0.FactorRGB     = GR_COMBINE_FACTOR_LOCAL;
+            tex0.FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
+            tex0.FactorAlpha   = GR_COMBINE_FACTOR_LOCAL;
+         }
+
+         colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         colorComb.Factor   = GR_COMBINE_FACTOR_LOCAL;
+         colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+
+         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_LOCAL;
+         alphaComb.Other    = GR_COMBINE_OTHER_TEXTURE;
         break;
       }
    case (FX_UM_E0_REPLACE | FX_UM_E1_BLEND):   /* Only for GLQuake */
-      if (tmu1 == FX_TMU1) {
-        grTexCombine(GR_TMU1,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE, FXTRUE, FXFALSE);
-
-        grTexCombine(GR_TMU0,
-                               GR_COMBINE_FUNCTION_BLEND_OTHER,
-                               GR_COMBINE_FACTOR_LOCAL,
-                               GR_COMBINE_FUNCTION_BLEND_OTHER,
-                               GR_COMBINE_FACTOR_LOCAL, FXFALSE, FXFALSE);
+      if (tmu0 == FX_TMU1) {
+         tex1.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+         tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+         tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+         tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+         tex1.InvertRGB     = FXTRUE;
+
+         tex0.FunctionRGB   = GR_COMBINE_FUNCTION_BLEND_OTHER;
+         tex0.FactorRGB     = GR_COMBINE_FACTOR_LOCAL;
+         tex0.FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
+         tex0.FactorAlpha   = GR_COMBINE_FACTOR_LOCAL;
       }
       else {
-        grTexCombine(GR_TMU1,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE);
-
-        grTexCombine(GR_TMU0,
-                               GR_COMBINE_FUNCTION_BLEND_OTHER,
-                               GR_COMBINE_FACTOR_ONE_MINUS_LOCAL,
-                               GR_COMBINE_FUNCTION_BLEND_OTHER,
-                               GR_COMBINE_FACTOR_ONE_MINUS_LOCAL,
-                               FXFALSE, FXFALSE);
-      }
-
-      grAlphaCombine(GR_COMBINE_FUNCTION_LOCAL,
-                              GR_COMBINE_FACTOR_NONE,
-                              locala, GR_COMBINE_OTHER_NONE, FXFALSE);
-
-      grColorCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                              GR_COMBINE_FACTOR_ONE,
-                              localc, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
+         tex1.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+         tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+         tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+         tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+
+         tex0.FunctionRGB   = GR_COMBINE_FUNCTION_BLEND_OTHER;
+         tex0.FactorRGB     = GR_COMBINE_FACTOR_ONE_MINUS_LOCAL;
+         tex0.FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
+         tex0.FactorAlpha   = GR_COMBINE_FACTOR_ONE_MINUS_LOCAL;
+      }
+
+      alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+      alphaComb.Factor   = GR_COMBINE_FACTOR_NONE;
+      alphaComb.Other    = GR_COMBINE_OTHER_NONE;
+
+      colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+      colorComb.Factor   = GR_COMBINE_FACTOR_ONE;
+      colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
       break;
    case (FX_UM_E0_REPLACE | FX_UM_E1_MODULATE):        /* Quake 2 and 3 */
       if (tmu1 == FX_TMU1) {
-        grTexCombine(GR_TMU1,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE,
-                               GR_COMBINE_FUNCTION_ZERO,
-                               GR_COMBINE_FACTOR_NONE, FXFALSE, FXTRUE);
-
-        grTexCombine(GR_TMU0,
-                               GR_COMBINE_FUNCTION_BLEND_OTHER,
-                               GR_COMBINE_FACTOR_LOCAL,
-                               GR_COMBINE_FUNCTION_BLEND_OTHER,
-                               GR_COMBINE_FACTOR_LOCAL, FXFALSE, FXFALSE);
-
+         tex1.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+         tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+         tex1.FunctionAlpha = GR_COMBINE_FUNCTION_ZERO;
+         tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+         tex1.InvertAlpha   = FXTRUE;
+
+         tex0.FunctionRGB   = GR_COMBINE_FUNCTION_BLEND_OTHER;
+         tex0.FactorRGB     = GR_COMBINE_FACTOR_LOCAL;
+         tex0.FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
+         tex0.FactorAlpha   = GR_COMBINE_FACTOR_LOCAL;
       }
       else {
-        grTexCombine(GR_TMU1,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE,
-                               GR_COMBINE_FUNCTION_LOCAL,
-                               GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE);
-
-        grTexCombine(GR_TMU0,
-                               GR_COMBINE_FUNCTION_BLEND_OTHER,
-                               GR_COMBINE_FACTOR_LOCAL,
-                               GR_COMBINE_FUNCTION_BLEND_OTHER,
-                               GR_COMBINE_FACTOR_ONE, FXFALSE, FXFALSE);
-      }
-
-      if (ti0->baseLevelInternalFormat == GL_RGB)
-        grAlphaCombine(GR_COMBINE_FUNCTION_LOCAL,
-                                 GR_COMBINE_FACTOR_NONE,
-                                 locala, GR_COMBINE_OTHER_NONE, FXFALSE);
-      else
-        grAlphaCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                                 GR_COMBINE_FACTOR_ONE,
-                                 locala, GR_COMBINE_OTHER_NONE, FXFALSE);
+         tex1.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+         tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+         tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+         tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+
+         tex0.FunctionRGB   = GR_COMBINE_FUNCTION_BLEND_OTHER;
+         tex0.FactorRGB     = GR_COMBINE_FACTOR_LOCAL;
+         tex0.FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
+         tex0.FactorAlpha   = GR_COMBINE_FACTOR_ONE;
+      }
 
+      if (ti0->baseLevelInternalFormat == GL_RGB) {
+         alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_NONE;
+         alphaComb.Other    = GR_COMBINE_OTHER_NONE;
+      } else {
+         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_ONE;
+         alphaComb.Other    = GR_COMBINE_OTHER_NONE;
+      }
 
-      grColorCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                              GR_COMBINE_FACTOR_ONE,
-                              localc, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
+      colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+      colorComb.Factor   = GR_COMBINE_FACTOR_ONE;
+      colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
       break;
 
 
@@ -901,45 +1070,145 @@ fxSetupTextureDoubleTMU_NoLock(GLcontext * ctx)
         isalpha[tmu0] = (ti0->baseLevelInternalFormat == GL_ALPHA);
         isalpha[tmu1] = (ti1->baseLevelInternalFormat == GL_ALPHA);
 
-        if (isalpha[FX_TMU1])
-           grTexCombine(GR_TMU1,
-                                  GR_COMBINE_FUNCTION_ZERO,
-                                  GR_COMBINE_FACTOR_NONE,
-                                  GR_COMBINE_FUNCTION_LOCAL,
-                                  GR_COMBINE_FACTOR_NONE, FXTRUE, FXFALSE);
-        else
-           grTexCombine(GR_TMU1,
-                                  GR_COMBINE_FUNCTION_LOCAL,
-                                  GR_COMBINE_FACTOR_NONE,
-                                  GR_COMBINE_FUNCTION_LOCAL,
-                                  GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE);
-
-        if (isalpha[FX_TMU0])
-           grTexCombine(GR_TMU0,
-                                  GR_COMBINE_FUNCTION_SCALE_OTHER,
-                                  GR_COMBINE_FACTOR_ONE,
-                                  GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL,
-                                  GR_COMBINE_FACTOR_ONE, FXFALSE, FXFALSE);
-        else
-           grTexCombine(GR_TMU0,
-                                  GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL,
-                                  GR_COMBINE_FACTOR_ONE,
-                                  GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL,
-                                  GR_COMBINE_FACTOR_ONE, FXFALSE, FXFALSE);
-
-        grColorCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                                 GR_COMBINE_FACTOR_LOCAL,
-                                 localc, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
-
-        grAlphaCombine(GR_COMBINE_FUNCTION_SCALE_OTHER,
-                                 GR_COMBINE_FACTOR_LOCAL,
-                                 locala, GR_COMBINE_OTHER_TEXTURE, FXFALSE);
+        if (isalpha[FX_TMU1]) {
+            tex1.FunctionRGB   = GR_COMBINE_FUNCTION_ZERO;
+            tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+            tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+            tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+            tex1.InvertRGB     = FXTRUE;
+        } else {
+            tex1.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+            tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+            tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+            tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+         }
+
+        if (isalpha[FX_TMU0]) {
+            tex0.FunctionRGB   = GR_COMBINE_FUNCTION_SCALE_OTHER;
+            tex0.FactorRGB     = GR_COMBINE_FACTOR_ONE;
+            tex0.FunctionAlpha = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
+            tex0.FactorAlpha   = GR_COMBINE_FACTOR_ONE;
+        } else {
+            tex0.FunctionRGB   = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
+            tex0.FactorRGB     = GR_COMBINE_FACTOR_ONE;
+            tex0.FunctionAlpha = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
+            tex0.FactorAlpha   = GR_COMBINE_FACTOR_ONE;
+         }
+
+         colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         colorComb.Factor   = GR_COMBINE_FACTOR_LOCAL;
+         colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+
+         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_LOCAL;
+         alphaComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+        break;
+      }
+
+   case (FX_UM_E0_REPLACE | FX_UM_E1_ADD):     /* Vulpine Sky */
+      {
+        GLboolean isalpha[FX_NUM_TMU];
+
+        isalpha[tmu0] = (ti0->baseLevelInternalFormat == GL_ALPHA);
+        isalpha[tmu1] = (ti1->baseLevelInternalFormat == GL_ALPHA);
+
+        if (isalpha[FX_TMU1]) {
+            tex1.FunctionRGB   = GR_COMBINE_FUNCTION_ZERO;
+            tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+            tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+            tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+            tex1.InvertRGB     = FXTRUE;
+        } else {
+            tex1.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+            tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+            tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+            tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+         }
+
+        if (isalpha[FX_TMU0]) {
+            tex0.FunctionRGB   = GR_COMBINE_FUNCTION_SCALE_OTHER;
+            tex0.FactorRGB     = GR_COMBINE_FACTOR_ONE;
+            tex0.FunctionAlpha = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
+            tex0.FactorAlpha   = GR_COMBINE_FACTOR_ONE;
+        } else {
+            tex0.FunctionRGB   = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
+            tex0.FactorRGB     = GR_COMBINE_FACTOR_ONE;
+            tex0.FunctionAlpha = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
+            tex0.FactorAlpha   = GR_COMBINE_FACTOR_ONE;
+         }
+
+         colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         colorComb.Factor   = GR_COMBINE_FACTOR_ONE;
+         colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+
+         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+         alphaComb.Factor   = GR_COMBINE_FACTOR_ONE;
+         alphaComb.Other    = GR_COMBINE_OTHER_TEXTURE;
         break;
       }
+
+   case (FX_UM_E0_MODULATE | FX_UM_E1_REPLACE): /* Homeworld2 */
+      {
+         tex1.FunctionRGB   = GR_COMBINE_FUNCTION_ZERO;
+         tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+         tex1.FunctionAlpha = GR_COMBINE_FUNCTION_ZERO;
+         tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+
+         tex0.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
+         tex0.FactorRGB     = GR_COMBINE_FACTOR_NONE;
+         tex0.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
+         tex0.FactorAlpha   = GR_COMBINE_FACTOR_NONE;
+
+         if (ifmt & (FX_UM_E0_RGB | FX_UM_E0_LUMINANCE)) {
+            alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+            alphaComb.Factor   = GR_COMBINE_FACTOR_NONE;
+            alphaComb.Other    = GR_COMBINE_OTHER_NONE;
+         } else {
+            alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+            alphaComb.Factor   = GR_COMBINE_FACTOR_ONE;
+            alphaComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+         }
+
+         if (ifmt & FX_UM_E0_ALPHA) {
+            colorComb.Function = GR_COMBINE_FUNCTION_LOCAL;
+            colorComb.Factor   = GR_COMBINE_FACTOR_NONE;
+            colorComb.Other    = GR_COMBINE_OTHER_NONE;
+         } else {
+            colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
+            colorComb.Factor   = GR_COMBINE_FACTOR_ONE;
+            colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
+         }
+         break;
+      }
    default:
-      fprintf(stderr, "%s: Unexpected dual texture mode encountered\n", __FUNCTION__);
-      break;
+      fprintf(stderr, "fxSetupTextureDoubleTMU_NoLock: Unexpected dual texture mode encountered\n");
+      return;
    }
+
+   grAlphaCombine(alphaComb.Function,
+                  alphaComb.Factor,
+                  alphaComb.Local,
+                  alphaComb.Other,
+                  alphaComb.Invert);
+   grColorCombine(colorComb.Function,
+                  colorComb.Factor,
+                  colorComb.Local,
+                  colorComb.Other,
+                  colorComb.Invert);
+   grTexCombine(GR_TMU0,
+                tex0.FunctionRGB,
+                tex0.FactorRGB,
+                tex0.FunctionAlpha,
+                tex0.FactorAlpha,
+                tex0.InvertRGB,
+                tex0.InvertAlpha);
+   grTexCombine(GR_TMU1,
+                tex1.FunctionRGB,
+                tex1.FactorRGB,
+                tex1.FunctionAlpha,
+                tex1.FactorAlpha,
+                tex1.InvertRGB,
+                tex1.InvertAlpha);
 }
 
 /************************* No Texture ***************************/
@@ -951,7 +1220,7 @@ fxSetupTextureNone_NoLock(GLcontext * ctx)
    GrCombineLocal_t localc, locala;
 
    if (TDFX_DEBUG & VERBOSE_DRIVER) {
-      fprintf(stderr, "%s(...)\n", __FUNCTION__);
+      fprintf(stderr, "fxSetupTextureNone_NoLock(...)\n");
    }
 
    if ((ctx->Light.ShadeModel == GL_SMOOTH) || 1 ||
@@ -967,16 +1236,22 @@ fxSetupTextureNone_NoLock(GLcontext * ctx)
       localc = GR_COMBINE_LOCAL_CONSTANT;
 
    grAlphaCombine(GR_COMBINE_FUNCTION_LOCAL,
-                           GR_COMBINE_FACTOR_NONE,
-                           locala, GR_COMBINE_OTHER_NONE, FXFALSE);
+                  GR_COMBINE_FACTOR_NONE,
+                  locala,
+                  GR_COMBINE_OTHER_NONE,
+                  FXFALSE);
 
    grColorCombine(GR_COMBINE_FUNCTION_LOCAL,
-                           GR_COMBINE_FACTOR_NONE,
-                           localc, GR_COMBINE_OTHER_NONE, FXFALSE);
+                  GR_COMBINE_FACTOR_NONE,
+                  localc,
+                  GR_COMBINE_OTHER_NONE,
+                  FXFALSE);
 
    fxMesa->lastUnitsMode = FX_UM_NONE;
 }
 
+#include "fxsetup.h"
+
 /************************************************************************/
 /************************** Texture Mode SetUp **************************/
 /************************************************************************/
@@ -987,27 +1262,45 @@ fxSetupTexture_NoLock(GLcontext * ctx)
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
 
    if (TDFX_DEBUG & VERBOSE_DRIVER) {
-      fprintf(stderr, "%s(...)\n", __FUNCTION__);
+      fprintf(stderr, "fxSetupTexture_NoLock(...)\n");
    }
 
-   /* Texture Combine, Color Combine and Alpha Combine. */
-   if (ctx->Texture.Unit[0]._ReallyEnabled == TEXTURE_2D_BIT &&
-       ctx->Texture.Unit[1]._ReallyEnabled == TEXTURE_2D_BIT &&
-       fxMesa->haveTwoTMUs) {
-      fxSetupTextureDoubleTMU_NoLock(ctx);
-   }
-   else if (ctx->Texture.Unit[0]._ReallyEnabled == TEXTURE_2D_BIT) {
-      fxSetupTextureSingleTMU_NoLock(ctx, 0);
-   }
-   else if (ctx->Texture.Unit[1]._ReallyEnabled == TEXTURE_2D_BIT) {
-      fxSetupTextureSingleTMU_NoLock(ctx, 1);
-   }
-   else {
-      fxSetupTextureNone_NoLock(ctx);
+   if (fxMesa->HaveCmbExt) {
+      /* Texture Combine, Color Combine and Alpha Combine. */
+      if ((ctx->Texture.Unit[0]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) &&
+          (ctx->Texture.Unit[1]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) &&
+          fxMesa->haveTwoTMUs) {
+         fxSetupTextureDoubleTMUNapalm_NoLock(ctx);
+      }
+      else if (ctx->Texture.Unit[0]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) {
+         fxSetupTextureSingleTMUNapalm_NoLock(ctx, 0);
+      }
+      else if (ctx->Texture.Unit[1]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) {
+         fxSetupTextureSingleTMUNapalm_NoLock(ctx, 1);
+      }
+      else {
+         fxSetupTextureNoneNapalm_NoLock(ctx);
+      }
+   } else {
+      /* Texture Combine, Color Combine and Alpha Combine. */
+      if ((ctx->Texture.Unit[0]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) &&
+          (ctx->Texture.Unit[1]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) &&
+          fxMesa->haveTwoTMUs) {
+         fxSetupTextureDoubleTMU_NoLock(ctx);
+      }
+      else if (ctx->Texture.Unit[0]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) {
+         fxSetupTextureSingleTMU_NoLock(ctx, 0);
+      }
+      else if (ctx->Texture.Unit[1]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) {
+         fxSetupTextureSingleTMU_NoLock(ctx, 1);
+      }
+      else {
+         fxSetupTextureNone_NoLock(ctx);
+      }
    }
 }
 
-static void
+void
 fxSetupTexture(GLcontext * ctx)
 {
    BEGIN_BOARD_LOCK();
@@ -1019,117 +1312,185 @@ fxSetupTexture(GLcontext * ctx)
 /**************************** Blend SetUp *******************************/
 /************************************************************************/
 
-/* XXX consider supporting GL_INGR_blend_func_separate */
 void
-fxDDBlendFunc(GLcontext * ctx, GLenum sfactor, GLenum dfactor)
+fxDDBlendFuncSeparate(GLcontext * ctx, GLenum sfactor, GLenum dfactor, GLenum asfactor, GLenum adfactor)
 {
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
    tfxUnitsState *us = &fxMesa->unitsState;
+   GLboolean isNapalm = (fxMesa->type >= GR_SSTTYPE_Voodoo4);
+   GLboolean have32bpp = (fxMesa->colDepth == 32);
+   GLboolean haveAlpha = fxMesa->haveHwAlpha;
    GrAlphaBlendFnc_t sfact, dfact, asfact, adfact;
 
-   /* From the Glide documentation:
-      For alpha source and destination blend function factor
-      parameters, Voodoo Graphics supports only
-      GR_BLEND_ZERO and GR_BLEND_ONE.
+   /*
+    * 15/16 BPP alpha channel alpha blending modes
+    *   0x0    AZERO           Zero
+    *   0x4    AONE            One
+    *
+    * 32 BPP alpha channel alpha blending modes
+    *   0x0    AZERO           Zero
+    *   0x1    ASRC_ALPHA      Source alpha
+    *   0x3    ADST_ALPHA      Destination alpha
+    *   0x4    AONE            One
+    *   0x5    AOMSRC_ALPHA    1 - Source alpha
+    *   0x7    AOMDST_ALPHA    1 - Destination alpha
+    *
+    * If we don't have HW alpha buffer:
+    *   DST_ALPHA == 1
+    *   ONE_MINUS_DST_ALPHA == 0
+    * Unsupported modes are:
+    *   1 if used as src blending factor
+    *   0 if used as dst blending factor
     */
 
    switch (sfactor) {
    case GL_ZERO:
-      asfact = sfact = GR_BLEND_ZERO;
+      sfact = GR_BLEND_ZERO;
       break;
    case GL_ONE:
-      asfact = sfact = GR_BLEND_ONE;
+      sfact = GR_BLEND_ONE;
       break;
    case GL_DST_COLOR:
       sfact = GR_BLEND_DST_COLOR;
-      asfact = GR_BLEND_ONE;
       break;
    case GL_ONE_MINUS_DST_COLOR:
       sfact = GR_BLEND_ONE_MINUS_DST_COLOR;
-      asfact = GR_BLEND_ONE;
       break;
    case GL_SRC_ALPHA:
       sfact = GR_BLEND_SRC_ALPHA;
-      asfact = GR_BLEND_ONE;
       break;
    case GL_ONE_MINUS_SRC_ALPHA:
       sfact = GR_BLEND_ONE_MINUS_SRC_ALPHA;
-      asfact = GR_BLEND_ONE;
       break;
    case GL_DST_ALPHA:
-      sfact = GR_BLEND_DST_ALPHA;
-      asfact = GR_BLEND_ONE;
+      sfact = haveAlpha ? GR_BLEND_DST_ALPHA : GR_BLEND_ONE/*bad*/;
       break;
    case GL_ONE_MINUS_DST_ALPHA:
-      sfact = GR_BLEND_ONE_MINUS_DST_ALPHA;
-      asfact = GR_BLEND_ONE;
+      sfact = haveAlpha ? GR_BLEND_ONE_MINUS_DST_ALPHA : GR_BLEND_ZERO/*bad*/;
       break;
    case GL_SRC_ALPHA_SATURATE:
       sfact = GR_BLEND_ALPHA_SATURATE;
-      asfact = GR_BLEND_ONE;
       break;
    case GL_SRC_COLOR:
+      if (isNapalm) {
+         sfact = GR_BLEND_SAME_COLOR_EXT;
+         break;
+      }
    case GL_ONE_MINUS_SRC_COLOR:
-      /* USELESS */
-      asfact = sfact = GR_BLEND_ONE;
-      break;
+      if (isNapalm) {
+         sfact = GR_BLEND_ONE_MINUS_SAME_COLOR_EXT;
+         break;
+      }
    default:
-      asfact = sfact = GR_BLEND_ONE;
+      sfact = GR_BLEND_ONE;
       break;
    }
 
-   if ((sfact != us->blendSrcFuncRGB) || (asfact != us->blendSrcFuncAlpha)) {
-      us->blendSrcFuncRGB = sfact;
-      us->blendSrcFuncAlpha = asfact;
-      fxMesa->new_state |= FX_NEW_BLEND;
+   switch (asfactor) {
+   case GL_ZERO:
+      asfact = GR_BLEND_ZERO;
+      break;
+   case GL_ONE:
+      asfact = GR_BLEND_ONE;
+      break;
+   case GL_SRC_COLOR:
+   case GL_SRC_ALPHA:
+      asfact = have32bpp ? GR_BLEND_SRC_ALPHA : GR_BLEND_ONE/*bad*/;
+      break;
+   case GL_ONE_MINUS_SRC_COLOR:
+   case GL_ONE_MINUS_SRC_ALPHA:
+      asfact = have32bpp ? GR_BLEND_ONE_MINUS_SRC_ALPHA : GR_BLEND_ONE/*bad*/;
+      break;
+   case GL_DST_COLOR:
+   case GL_DST_ALPHA:
+      asfact = (have32bpp && haveAlpha) ? GR_BLEND_DST_ALPHA : GR_BLEND_ONE/*bad*/;
+      break;
+   case GL_ONE_MINUS_DST_COLOR:
+   case GL_ONE_MINUS_DST_ALPHA:
+      asfact = (have32bpp && haveAlpha) ? GR_BLEND_ONE_MINUS_DST_ALPHA : GR_BLEND_ZERO/*bad*/;
+      break;
+   case GL_SRC_ALPHA_SATURATE:
+      asfact = GR_BLEND_ONE;
+      break;
+   default:
+      asfact = GR_BLEND_ONE;
+      break;
    }
 
    switch (dfactor) {
    case GL_ZERO:
-      adfact = dfact = GR_BLEND_ZERO;
+      dfact = GR_BLEND_ZERO;
       break;
    case GL_ONE:
-      adfact = dfact = GR_BLEND_ONE;
+      dfact = GR_BLEND_ONE;
       break;
    case GL_SRC_COLOR:
       dfact = GR_BLEND_SRC_COLOR;
-      adfact = GR_BLEND_ZERO;
       break;
    case GL_ONE_MINUS_SRC_COLOR:
       dfact = GR_BLEND_ONE_MINUS_SRC_COLOR;
-      adfact = GR_BLEND_ZERO;
       break;
    case GL_SRC_ALPHA:
       dfact = GR_BLEND_SRC_ALPHA;
-      adfact = GR_BLEND_ZERO;
       break;
    case GL_ONE_MINUS_SRC_ALPHA:
       dfact = GR_BLEND_ONE_MINUS_SRC_ALPHA;
-      adfact = GR_BLEND_ZERO;
       break;
    case GL_DST_ALPHA:
-      /* dfact=GR_BLEND_DST_ALPHA; */
-      /* We can't do DST_ALPHA */
-      dfact = GR_BLEND_ONE;
-      adfact = GR_BLEND_ZERO;
+      dfact = haveAlpha ? GR_BLEND_DST_ALPHA : GR_BLEND_ONE/*bad*/;
       break;
    case GL_ONE_MINUS_DST_ALPHA:
-      /* dfact=GR_BLEND_ONE_MINUS_DST_ALPHA; */
-      /* We can't do DST_ALPHA */
+      dfact = haveAlpha ? GR_BLEND_ONE_MINUS_DST_ALPHA : GR_BLEND_ZERO/*bad*/;
+      break;
+   case GL_DST_COLOR:
+      if (isNapalm) {
+         dfact = GR_BLEND_SAME_COLOR_EXT;
+         break;
+      }
+   case GL_ONE_MINUS_DST_COLOR:
+      if (isNapalm) {
+         dfact = GR_BLEND_ONE_MINUS_SAME_COLOR_EXT;
+         break;
+      }
+   default:
       dfact = GR_BLEND_ZERO;
+      break;
+   }
+
+   switch (adfactor) {
+   case GL_ZERO:
       adfact = GR_BLEND_ZERO;
       break;
-   case GL_SRC_ALPHA_SATURATE:
+   case GL_ONE:
+      adfact = GR_BLEND_ONE;
+      break;
+   case GL_SRC_COLOR:
+   case GL_SRC_ALPHA:
+      adfact = have32bpp ? GR_BLEND_SRC_ALPHA : GR_BLEND_ZERO/*bad*/;
+      break;
+   case GL_ONE_MINUS_SRC_COLOR:
+   case GL_ONE_MINUS_SRC_ALPHA:
+      adfact = have32bpp ? GR_BLEND_ONE_MINUS_SRC_ALPHA : GR_BLEND_ZERO/*bad*/;
+      break;
    case GL_DST_COLOR:
+   case GL_DST_ALPHA:
+      adfact = (have32bpp && haveAlpha) ? GR_BLEND_DST_ALPHA : GR_BLEND_ONE/*bad*/;
+      break;
    case GL_ONE_MINUS_DST_COLOR:
-      /* USELESS */
-      adfact = dfact = GR_BLEND_ZERO;
+   case GL_ONE_MINUS_DST_ALPHA:
+      adfact = (have32bpp && haveAlpha) ? GR_BLEND_ONE_MINUS_DST_ALPHA : GR_BLEND_ZERO/*bad*/;
       break;
    default:
-      adfact = dfact = GR_BLEND_ZERO;
+      adfact = GR_BLEND_ZERO;
       break;
    }
 
+   if ((sfact != us->blendSrcFuncRGB) || (asfact != us->blendSrcFuncAlpha)) {
+      us->blendSrcFuncRGB = sfact;
+      us->blendSrcFuncAlpha = asfact;
+      fxMesa->new_state |= FX_NEW_BLEND;
+   }
+
    if ((dfact != us->blendDstFuncRGB) || (adfact != us->blendDstFuncAlpha)) {
       us->blendDstFuncRGB = dfact;
       us->blendDstFuncAlpha = adfact;
@@ -1137,18 +1498,77 @@ fxDDBlendFunc(GLcontext * ctx, GLenum sfactor, GLenum dfactor)
    }
 }
 
-static void
-fxSetupBlend(GLcontext * ctx)
+void
+fxDDBlendEquationSeparate(GLcontext * ctx, GLenum modeRGB, GLenum modeA)
 {
-   fxMesaContext fxMesa = FX_CONTEXT(ctx);
-   tfxUnitsState *us = &fxMesa->unitsState;
+ fxMesaContext fxMesa = FX_CONTEXT(ctx);
+ tfxUnitsState *us = &fxMesa->unitsState;
+ GrAlphaBlendOp_t q;
+
+ switch (modeRGB) {
+        case GL_FUNC_ADD:
+             q = GR_BLEND_OP_ADD;
+             break;
+        case GL_FUNC_SUBTRACT:
+             q = GR_BLEND_OP_SUB;
+             break;
+        case GL_FUNC_REVERSE_SUBTRACT:
+             q = GR_BLEND_OP_REVSUB;
+             break;
+        default:
+             q = us->blendEqRGB;
+ }
+ if (q != us->blendEqRGB) {
+    us->blendEqRGB = q;
+    fxMesa->new_state |= FX_NEW_BLEND;
+ }
+
+ switch (modeA) {
+        case GL_FUNC_ADD:
+             q = GR_BLEND_OP_ADD;
+             break;
+        case GL_FUNC_SUBTRACT:
+             q = GR_BLEND_OP_SUB;
+             break;
+        case GL_FUNC_REVERSE_SUBTRACT:
+             q = GR_BLEND_OP_REVSUB;
+             break;
+        default:
+             q = us->blendEqAlpha;
+ }
+ if (q != us->blendEqAlpha) {
+    us->blendEqAlpha = q;
+    fxMesa->new_state |= FX_NEW_BLEND;
+ }
+}
 
-   if (us->blendEnabled)
-      grAlphaBlendFunction(us->blendSrcFuncRGB, us->blendDstFuncRGB,
-                             us->blendSrcFuncAlpha, us->blendDstFuncAlpha);
-   else
-      grAlphaBlendFunction(GR_BLEND_ONE, GR_BLEND_ZERO, GR_BLEND_ONE,
-                             GR_BLEND_ZERO);
+void
+fxSetupBlend(GLcontext * ctx)
+{
+ fxMesaContext fxMesa = FX_CONTEXT(ctx);
+ tfxUnitsState *us = &fxMesa->unitsState;
+
+ if (fxMesa->HavePixExt) {
+    if (us->blendEnabled) {
+       fxMesa->Glide.grAlphaBlendFunctionExt(us->blendSrcFuncRGB, us->blendDstFuncRGB,
+                                             us->blendEqRGB,
+                                             us->blendSrcFuncAlpha, us->blendDstFuncAlpha,
+                                             us->blendEqAlpha);
+    } else {
+       fxMesa->Glide.grAlphaBlendFunctionExt(GR_BLEND_ONE, GR_BLEND_ZERO,
+                                             GR_BLEND_OP_ADD,
+                                             GR_BLEND_ONE, GR_BLEND_ZERO,
+                                             GR_BLEND_OP_ADD);
+    }
+ } else {
+    if (us->blendEnabled) {
+       grAlphaBlendFunction(us->blendSrcFuncRGB, us->blendDstFuncRGB,
+                            us->blendSrcFuncAlpha, us->blendDstFuncAlpha);
+    } else {
+       grAlphaBlendFunction(GR_BLEND_ONE, GR_BLEND_ZERO,
+                            GR_BLEND_ONE, GR_BLEND_ZERO);
+    }
+ }
 }
 
 /************************************************************************/
@@ -1215,7 +1635,7 @@ fxDDDepthMask(GLcontext * ctx, GLboolean flag)
    }
 }
 
-static void
+void
 fxSetupDepthTest(GLcontext * ctx)
 {
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
@@ -1261,11 +1681,16 @@ static GrStencil_t convertGLStencilOp( GLenum op )
 }
 
 void
-fxDDStencilFunc (GLcontext *ctx, GLenum func, GLint ref, GLuint mask)
+fxDDStencilFuncSeparate (GLcontext *ctx, GLenum face, GLenum func,
+                         GLint ref, GLuint mask)
 {
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
    tfxUnitsState *us = &fxMesa->unitsState;
 
+   if (ctx->Stencil.ActiveFace) {
+      return;
+   }
+
    if (
        (us->stencilFunction != func)
        ||
@@ -1281,11 +1706,15 @@ fxDDStencilFunc (GLcontext *ctx, GLenum func, GLint ref, GLuint mask)
 }
 
 void
-fxDDStencilMask (GLcontext *ctx, GLuint mask)
+fxDDStencilMaskSeparate (GLcontext *ctx, GLenum face, GLuint mask)
 {
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
    tfxUnitsState *us = &fxMesa->unitsState;
 
+   if (ctx->Stencil.ActiveFace) {
+      return;
+   }
+
    if (us->stencilWriteMask != mask) {
       us->stencilWriteMask = mask;
       fxMesa->new_state |= FX_NEW_STENCIL;
@@ -1293,11 +1722,16 @@ fxDDStencilMask (GLcontext *ctx, GLuint mask)
 }
 
 void
-fxDDStencilOp (GLcontext *ctx, GLenum sfail, GLenum zfail, GLenum zpass)
+fxDDStencilOpSeparate (GLcontext *ctx, GLenum face, GLenum sfail,
+                       GLenum zfail, GLenum zpass)
 {
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
    tfxUnitsState *us = &fxMesa->unitsState;
 
+   if (ctx->Stencil.ActiveFace) {
+      return;
+   }
+
    if (
        (us->stencilFailFunc != sfail)
        ||
@@ -1312,17 +1746,25 @@ fxDDStencilOp (GLcontext *ctx, GLenum sfail, GLenum zfail, GLenum zpass)
    }
 }
 
-static void
+void
 fxSetupStencil (GLcontext * ctx)
 {
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
    tfxUnitsState *us = &fxMesa->unitsState;
 
    if (us->stencilEnabled) {
+      GrCmpFnc_t stencilFailFunc = GR_STENCILOP_KEEP;
+      GrCmpFnc_t stencilZFailFunc = GR_STENCILOP_KEEP;
+      GrCmpFnc_t stencilZPassFunc = GR_STENCILOP_KEEP;
+      if (!fxMesa->multipass) {
+         stencilFailFunc = convertGLStencilOp(us->stencilFailFunc);
+         stencilZFailFunc = convertGLStencilOp(us->stencilZFailFunc);
+         stencilZPassFunc = convertGLStencilOp(us->stencilZPassFunc);
+      }
       grEnable(GR_STENCIL_MODE_EXT);
-      fxMesa->Glide.grStencilOpExt(convertGLStencilOp(us->stencilFailFunc),
-                                   convertGLStencilOp(us->stencilZFailFunc),
-                                   convertGLStencilOp(us->stencilZPassFunc));
+      fxMesa->Glide.grStencilOpExt(stencilFailFunc,
+                                   stencilZFailFunc,
+                                   stencilZPassFunc);
       fxMesa->Glide.grStencilFuncExt(us->stencilFunction - GL_NEVER + GR_CMP_NEVER,
                                      us->stencilRefValue,
                                      us->stencilValueMask);
@@ -1332,29 +1774,38 @@ fxSetupStencil (GLcontext * ctx)
    }
 }
 
-/************************************************************************/
-/**************************** Color Mask SetUp **************************/
-/************************************************************************/
-
-void fxColorMask (fxMesaContext fxMesa, GLboolean enable)
+void
+fxSetupStencilFace (GLcontext * ctx, GLint face)
 {
-/* These are used in calls to FX_grColorMask() */
-static const FxBool false4[4] = { FXFALSE, FXFALSE, FXFALSE, FXFALSE };
-static const FxBool true4[4] = { FXTRUE, FXTRUE, FXTRUE, FXTRUE };
-
-   const FxBool *rgba = enable ? true4 : false4;
+   fxMesaContext fxMesa = FX_CONTEXT(ctx);
+   tfxUnitsState *us = &fxMesa->unitsState;
 
-   if (fxMesa->colDepth != 16) {
-      /* 32bpp mode or 15bpp mode */
-      fxMesa->Glide.grColorMaskExt(rgba[RCOMP], rgba[GCOMP],
-                                   rgba[BCOMP], rgba[ACOMP] && fxMesa->haveHwAlpha);
-   }
-   else {
-      /* 16 bpp mode */
-      grColorMask(rgba[RCOMP] || rgba[GCOMP] || rgba[BCOMP], rgba[ACOMP] && fxMesa->haveHwAlpha);
+   if (us->stencilEnabled) {
+      GrCmpFnc_t stencilFailFunc = GR_STENCILOP_KEEP;
+      GrCmpFnc_t stencilZFailFunc = GR_STENCILOP_KEEP;
+      GrCmpFnc_t stencilZPassFunc = GR_STENCILOP_KEEP;
+      if (!fxMesa->multipass) {
+         stencilFailFunc = convertGLStencilOp(ctx->Stencil.FailFunc[face]);
+         stencilZFailFunc = convertGLStencilOp(ctx->Stencil.ZFailFunc[face]);
+         stencilZPassFunc = convertGLStencilOp(ctx->Stencil.ZPassFunc[face]);
+      }
+      grEnable(GR_STENCIL_MODE_EXT);
+      fxMesa->Glide.grStencilOpExt(stencilFailFunc,
+                                   stencilZFailFunc,
+                                   stencilZPassFunc);
+      fxMesa->Glide.grStencilFuncExt(ctx->Stencil.Function[face] - GL_NEVER + GR_CMP_NEVER,
+                                     ctx->Stencil.Ref[face],
+                                     ctx->Stencil.ValueMask[face]);
+      fxMesa->Glide.grStencilMaskExt(ctx->Stencil.WriteMask[face]);
+   } else {
+      grDisable(GR_STENCIL_MODE_EXT);
    }
 }
 
+/************************************************************************/
+/**************************** Color Mask SetUp **************************/
+/************************************************************************/
+
 void
 fxDDColorMask(GLcontext * ctx,
              GLboolean r, GLboolean g, GLboolean b, GLboolean a)
@@ -1367,16 +1818,24 @@ fxDDColorMask(GLcontext * ctx,
    (void) a;
 }
 
-static void
+void
 fxSetupColorMask(GLcontext * ctx)
 {
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
 
-   if (ctx->Color.DrawBuffer == GL_NONE) {
-      fxColorMask(fxMesa, GL_FALSE);
+   if (fxMesa->colDepth == 32) {
+      /* 32bpp mode */
+      fxMesa->Glide.grColorMaskExt(ctx->Color.ColorMask[RCOMP],
+                                   ctx->Color.ColorMask[GCOMP],
+                                   ctx->Color.ColorMask[BCOMP],
+                                   ctx->Color.ColorMask[ACOMP] && fxMesa->haveHwAlpha);
    }
    else {
-      fxColorMask(fxMesa, GL_TRUE);
+      /* 15/16 bpp mode */
+      grColorMask(ctx->Color.ColorMask[RCOMP] |
+                  ctx->Color.ColorMask[GCOMP] |
+                  ctx->Color.ColorMask[BCOMP],
+                  ctx->Color.ColorMask[ACOMP] && fxMesa->haveHwAlpha);
    }
 }
 
@@ -1413,6 +1872,14 @@ fxSetupFog(GLcontext * ctx)
         case GL_LINEAR:
            guFogGenerateLinear(fxMesa->fogTable, ctx->Fog.Start,
                                ctx->Fog.End);
+           if (fxMesa->fogTable[0] > 63) {
+              /* [dBorca] Hack alert:
+               * As per Glide3 Programming Guide:
+               * The difference between consecutive fog values
+               * must be less than 64.
+               */
+              fxMesa->fogTable[0] = 63;
+           }
            break;
         case GL_EXP:
            guFogGenerateExp(fxMesa->fogTable, ctx->Fog.Density);
@@ -1430,7 +1897,15 @@ fxSetupFog(GLcontext * ctx)
       }
 
       grFogTable(fxMesa->fogTable);
-      grFogMode(GR_FOG_WITH_TABLE_ON_Q);
+      if (ctx->Fog.FogCoordinateSource == GL_FOG_COORDINATE_EXT) {
+         grVertexLayout(GR_PARAM_FOG_EXT, GR_VERTEX_FOG_OFFSET << 2,
+                                          GR_PARAM_ENABLE);
+         grFogMode(GR_FOG_WITH_TABLE_ON_FOGCOORD_EXT);
+      } else {
+         grVertexLayout(GR_PARAM_FOG_EXT, GR_VERTEX_FOG_OFFSET << 2,
+                                          GR_PARAM_DISABLE);
+         grFogMode(GR_FOG_WITH_TABLE_ON_Q);
+      }
    }
    else {
       grFogMode(GR_FOG_DISABLE);
@@ -1441,6 +1916,25 @@ void
 fxDDFogfv(GLcontext * ctx, GLenum pname, const GLfloat * params)
 {
    FX_CONTEXT(ctx)->new_state |= FX_NEW_FOG;
+   switch (pname) {
+      case GL_FOG_COORDINATE_SOURCE_EXT: {
+         GLenum p = (GLenum)*params;
+         if (p == GL_FOG_COORDINATE_EXT) {
+            _swrast_allow_vertex_fog(ctx, GL_TRUE);
+            _swrast_allow_pixel_fog(ctx, GL_FALSE);
+            _tnl_allow_vertex_fog( ctx, GL_TRUE);
+            _tnl_allow_pixel_fog( ctx, GL_FALSE);
+         } else {
+            _swrast_allow_vertex_fog(ctx, GL_FALSE);
+            _swrast_allow_pixel_fog(ctx, GL_TRUE);
+            _tnl_allow_vertex_fog( ctx, GL_FALSE);
+            _tnl_allow_pixel_fog( ctx, GL_TRUE);
+         }
+         break;
+      }
+      default:
+         ;
+   }
 }
 
 /************************************************************************/
@@ -1453,30 +1947,34 @@ fxSetScissorValues(GLcontext * ctx)
 {
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
    int xmin, xmax;
-   int ymin, ymax, check;
+   int ymin, ymax;
 
    if (ctx->Scissor.Enabled) {
       xmin = ctx->Scissor.X;
       xmax = ctx->Scissor.X + ctx->Scissor.Width;
       ymin = ctx->Scissor.Y;
       ymax = ctx->Scissor.Y + ctx->Scissor.Height;
-      check = 1;
+
+      if (xmin < 0)
+         xmin = 0;
+      if (xmax > fxMesa->width)
+         xmax = fxMesa->width;
+      if (ymin < fxMesa->screen_height - fxMesa->height)
+         ymin = fxMesa->screen_height - fxMesa->height;
+      if (ymax > fxMesa->screen_height - 0)
+         ymax = fxMesa->screen_height - 0;
    }
    else {
       xmin = 0;
       ymin = 0;
       xmax = fxMesa->width;
       ymax = fxMesa->height;
-      check = 0;
    }
-   if (xmin < fxMesa->clipMinX)
-      xmin = fxMesa->clipMinX;
-   if (xmax > fxMesa->clipMaxX)
-      xmax = fxMesa->clipMaxX;
-   if (ymin < fxMesa->screen_height - fxMesa->clipMaxY)
-      ymin = fxMesa->screen_height - fxMesa->clipMaxY;
-   if (ymax > fxMesa->screen_height - fxMesa->clipMinY)
-      ymax = fxMesa->screen_height - fxMesa->clipMinY;
+
+   fxMesa->clipMinX = xmin;
+   fxMesa->clipMinY = ymin;
+   fxMesa->clipMaxX = xmax;
+   fxMesa->clipMaxY = ymax;
    grClipWindow(xmin, ymin, xmax, ymax);
 }
 
@@ -1520,7 +2018,7 @@ fxSetupCull(GLcontext * ctx)
    fxMesaContext fxMesa = FX_CONTEXT(ctx);
    GrCullMode_t mode = GR_CULL_DISABLE;
 
-   if (ctx->Polygon.CullFlag) {
+   if (ctx->Polygon.CullFlag && (fxMesa->raster_primitive == GL_TRIANGLES)) {
       switch (ctx->Polygon.CullFaceMode) {
       case GL_BACK:
         if (ctx->Polygon.FrontFace == GL_CCW)
@@ -1543,9 +2041,6 @@ fxSetupCull(GLcontext * ctx)
       }
    }
 
-   /* KW: don't need to check raster_primitive here as we don't
-    * attempt to draw lines or points with triangles.
-    */
    if (fxMesa->cullMode != mode) {
       fxMesa->cullMode = mode;
       grCullMode(mode);
@@ -1564,7 +2059,7 @@ fxDDEnable(GLcontext * ctx, GLenum cap, GLboolean state)
    tfxUnitsState *us = &fxMesa->unitsState;
 
    if (TDFX_DEBUG & VERBOSE_DRIVER) {
-      fprintf(stderr, "%s(%s)\n", state ? __FUNCTION__ : "fxDDDisable",
+      fprintf(stderr, "%s(%s)\n", state ? "fxDDEnable" : "fxDDDisable",
              _mesa_lookup_enum_by_nr(cap));
    }
 
@@ -1617,6 +2112,7 @@ fxDDEnable(GLcontext * ctx, GLenum cap, GLboolean state)
    case GL_LINE_STIPPLE:
    case GL_POINT_SMOOTH:
    case GL_POLYGON_SMOOTH:
+   case GL_TEXTURE_1D:
    case GL_TEXTURE_2D:
       fxMesa->new_state |= FX_NEW_TEXTURING;
       break;
@@ -1670,7 +2166,7 @@ fxSetupFXUnits(GLcontext * ctx)
    GLuint newstate = fxMesa->new_state;
 
    if (TDFX_DEBUG & VERBOSE_DRIVER)
-      fx_print_state_flags(__FUNCTION__, newstate);
+      fx_print_state_flags("fxSetupFXUnits", newstate);
 
    if (newstate) {
       if (newstate & FX_NEW_TEXTURING)