/*
 * Mesa 3-D graphics library
 * Version:  4.0
 *
 * Copyright (C) 1999-2001  Brian Paul   All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/* Authors:
 *    David Bucciarelli
 *    Brian Paul
 *    Daryll Strauss
 *    Keith Whitwell
 *    Daniel Borca
 *    Hiroshi Morii
 */

/* fxsetup.c - 3Dfx VooDoo rendering mode setup functions */


#ifdef HAVE_CONFIG_H
#include "conf.h"
#endif

#if defined(FX)

#include "fxdrv.h"
#include "enums.h"
#include "tnl/t_context.h"

static void
fxTexValidate(GLcontext * ctx, struct gl_texture_object *tObj)
{
   tfxTexInfo *ti = fxTMGetTexInfo(tObj);
   GLint minl, maxl;

   if (ti->validated) {
      if (TDFX_DEBUG & VERBOSE_DRIVER) {
	 fprintf(stderr, "fxTexValidate(NOP)\n");
      }
      return;
   }

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      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][0]->MaxLog2);

#if FX_RESCALE_BIG_TEXURES_HACK
{
   extern void _mesa_rescale_teximage2d( GLuint bytesPerPixel,
                                         GLuint dstRowStride,
                                         GLint srcWidth, GLint srcHeight,
                                         GLint dstWidth, GLint dstHeight,
                                         const GLvoid *srcImage, GLvoid *dstImage );
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   /* [dBorca]
    * Ooooooook! Here's a(nother) long story.
    * We get here because we need to handle a texture larger
    * than hardware can support. Two cases:
    * 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.
    * NB: we could handle mml->[wh]Scale nicely, using (biased) shifts.
    *
    * Which brings me to another issue. How can we handle NPOT textures?
    * - rescaling NPOT to the next bigger POT (mml->[wh]Scale can't shift)
    * - upping the max LOD to the next power-of-two, in fxTexGetInfo; then
    *   choosing non-power-of-two values for ti->[st]Scale... Anyhow, we
    *   still need to align mipmaps correctly in texture memory!
    */
   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_PBUFFER_ALLOC(_w * _h * texelBytes);
         _mesa_rescale_teximage2d(texelBytes,
                                  _w * texelBytes, /* dst stride */
                                  mml->width, mml->height, /* src */
                                  _w, _h, /* dst */
                                  texImage_Data /*src*/, texImage->Data /*dst*/ );
         MESA_PBUFFER_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[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[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);

   ti->baseLevelInternalFormat = tObj->Image[0][minl]->Format;

   ti->validated = GL_TRUE;

   ti->info.data = NULL;
}

static void
fxPrintUnitsMode(const char *msg, GLuint mode)
{
   fprintf(stderr,
	   "%s: (0x%x) %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
	   msg,
	   mode,
	   (mode & FX_UM_E0_REPLACE) ? "E0_REPLACE, " : "",
	   (mode & FX_UM_E0_MODULATE) ? "E0_MODULATE, " : "",
	   (mode & FX_UM_E0_DECAL) ? "E0_DECAL, " : "",
	   (mode & FX_UM_E0_BLEND) ? "E0_BLEND, " : "",
	   (mode & FX_UM_E1_REPLACE) ? "E1_REPLACE, " : "",
	   (mode & FX_UM_E1_MODULATE) ? "E1_MODULATE, " : "",
	   (mode & FX_UM_E1_DECAL) ? "E1_DECAL, " : "",
	   (mode & FX_UM_E1_BLEND) ? "E1_BLEND, " : "",
	   (mode & FX_UM_E0_ALPHA) ? "E0_ALPHA, " : "",
	   (mode & FX_UM_E0_LUMINANCE) ? "E0_LUMINANCE, " : "",
	   (mode & FX_UM_E0_LUMINANCE_ALPHA) ? "E0_LUMINANCE_ALPHA, " : "",
	   (mode & FX_UM_E0_INTENSITY) ? "E0_INTENSITY, " : "",
	   (mode & FX_UM_E0_RGB) ? "E0_RGB, " : "",
	   (mode & FX_UM_E0_RGBA) ? "E0_RGBA, " : "",
	   (mode & FX_UM_E1_ALPHA) ? "E1_ALPHA, " : "",
	   (mode & FX_UM_E1_LUMINANCE) ? "E1_LUMINANCE, " : "",
	   (mode & FX_UM_E1_LUMINANCE_ALPHA) ? "E1_LUMINANCE_ALPHA, " : "",
	   (mode & FX_UM_E1_INTENSITY) ? "E1_INTENSITY, " : "",
	   (mode & FX_UM_E1_RGB) ? "E1_RGB, " : "",
	   (mode & FX_UM_E1_RGBA) ? "E1_RGBA, " : "",
	   (mode & FX_UM_COLOR_ITERATED) ? "COLOR_ITERATED, " : "",
	   (mode & FX_UM_COLOR_CONSTANT) ? "COLOR_CONSTANT, " : "",
	   (mode & FX_UM_ALPHA_ITERATED) ? "ALPHA_ITERATED, " : "",
	   (mode & FX_UM_ALPHA_CONSTANT) ? "ALPHA_CONSTANT, " : "");
}

static GLuint
fxGetTexSetConfiguration(GLcontext * ctx,
			 struct gl_texture_object *tObj0,
			 struct gl_texture_object *tObj1)
{
   GLuint unitsmode = 0;
   GLuint envmode = 0;
   GLuint ifmt = 0;

   if ((ctx->Light.ShadeModel == GL_SMOOTH) || 1 ||
       (ctx->Point.SmoothFlag) ||
       (ctx->Line.SmoothFlag) ||
       (ctx->Polygon.SmoothFlag)) unitsmode |= FX_UM_ALPHA_ITERATED;
   else
      unitsmode |= FX_UM_ALPHA_CONSTANT;

   if (ctx->Light.ShadeModel == GL_SMOOTH || 1)
      unitsmode |= FX_UM_COLOR_ITERATED;
   else
      unitsmode |= FX_UM_COLOR_CONSTANT;



   /* 
      OpenGL Feeds Texture 0 into Texture 1
      Glide Feeds Texture 1 into Texture 0
    */
   if (tObj0) {
      tfxTexInfo *ti0 = fxTMGetTexInfo(tObj0);

      switch (ti0->baseLevelInternalFormat) {
      case GL_ALPHA:
	 ifmt |= FX_UM_E0_ALPHA;
	 break;
      case GL_LUMINANCE:
	 ifmt |= FX_UM_E0_LUMINANCE;
	 break;
      case GL_LUMINANCE_ALPHA:
	 ifmt |= FX_UM_E0_LUMINANCE_ALPHA;
	 break;
      case GL_INTENSITY:
	 ifmt |= FX_UM_E0_INTENSITY;
	 break;
      case GL_RGB:
	 ifmt |= FX_UM_E0_RGB;
	 break;
      case GL_RGBA:
	 ifmt |= FX_UM_E0_RGBA;
	 break;
      }

      switch (ctx->Texture.Unit[0].EnvMode) {
      case GL_DECAL:
	 envmode |= FX_UM_E0_DECAL;
	 break;
      case GL_MODULATE:
	 envmode |= FX_UM_E0_MODULATE;
	 break;
      case GL_REPLACE:
	 envmode |= FX_UM_E0_REPLACE;
	 break;
      case GL_BLEND:
	 envmode |= FX_UM_E0_BLEND;
	 break;
      case GL_ADD:
	 envmode |= FX_UM_E0_ADD;
	 break;
      default:
	 /* do nothing */
	 break;
      }
   }

   if (tObj1) {
      tfxTexInfo *ti1 = fxTMGetTexInfo(tObj1);

      switch (ti1->baseLevelInternalFormat) {
      case GL_ALPHA:
	 ifmt |= FX_UM_E1_ALPHA;
	 break;
      case GL_LUMINANCE:
	 ifmt |= FX_UM_E1_LUMINANCE;
	 break;
      case GL_LUMINANCE_ALPHA:
	 ifmt |= FX_UM_E1_LUMINANCE_ALPHA;
	 break;
      case GL_INTENSITY:
	 ifmt |= FX_UM_E1_INTENSITY;
	 break;
      case GL_RGB:
	 ifmt |= FX_UM_E1_RGB;
	 break;
      case GL_RGBA:
	 ifmt |= FX_UM_E1_RGBA;
	 break;
      default:
	 /* do nothing */
	 break;
      }

      switch (ctx->Texture.Unit[1].EnvMode) {
      case GL_DECAL:
	 envmode |= FX_UM_E1_DECAL;
	 break;
      case GL_MODULATE:
	 envmode |= FX_UM_E1_MODULATE;
	 break;
      case GL_REPLACE:
	 envmode |= FX_UM_E1_REPLACE;
	 break;
      case GL_BLEND:
	 envmode |= FX_UM_E1_BLEND;
	 break;
      case GL_ADD:
	 envmode |= FX_UM_E1_ADD;
	 break;
      default:
	 /* do nothing */
	 break;
      }
   }

   unitsmode |= (ifmt | envmode);

   if (TDFX_DEBUG & (VERBOSE_DRIVER | VERBOSE_TEXTURE))
      fxPrintUnitsMode("fxGetTexSetConfiguration", unitsmode);

   return unitsmode;
}

/************************************************************************/
/************************* Rendering Mode SetUp *************************/
/************************************************************************/

/************************* Single Texture Set ***************************/

static void
fxSetupSingleTMU_NoLock(fxMesaContext fxMesa, struct gl_texture_object *tObj)
{
   tfxTexInfo *ti = fxTMGetTexInfo(tObj);
   int tmu;

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "fxSetupSingleTMU_NoLock(%p (%d))\n", (void *)tObj, tObj->Name);
   }

#if 1 /* [dBorca] Good... bad... I'm the guy with the gun! */
   ti->lastTimeUsed = fxMesa->texBindNumber;
#endif

   /* Make sure we're not loaded incorrectly */
   if (ti->isInTM) {
      if (ti->LODblend) {
	 if (ti->whichTMU != FX_TMU_SPLIT)
	    fxTMMoveOutTM(fxMesa, tObj);
      }
      else {
	 if (ti->whichTMU == FX_TMU_SPLIT)
	    fxTMMoveOutTM(fxMesa, tObj);
      }
   }

   /* Make sure we're loaded correctly */
   if (!ti->isInTM) {
      if (ti->LODblend)
	 fxTMMoveInTM_NoLock(fxMesa, tObj, FX_TMU_SPLIT);
      else {
	 if (fxMesa->haveTwoTMUs) {
            if (fxTMCheckStartAddr(fxMesa, FX_TMU0, ti)) {
	       fxTMMoveInTM_NoLock(fxMesa, tObj, FX_TMU0);
	    }
	    else {
	       fxTMMoveInTM_NoLock(fxMesa, tObj, FX_TMU1);
	    }
	 }
	 else
	    fxTMMoveInTM_NoLock(fxMesa, tObj, FX_TMU0);
      }
   }

   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, "fxSetupSingleTMU_NoLock: uploading texture palette\n");
	 }
	 grTexDownloadTable(ti->paltype, &(ti->palette));
      }
#if FX_TC_NCC
      if ((ti->info.format == GR_TEXFMT_AYIQ_8422) ||
          (ti->info.format == GR_TEXFMT_YIQ_422)) {
	 if (TDFX_DEBUG & VERBOSE_DRIVER) {
	    fprintf(stderr, "fxSetupSingleTMU_NoLock: uploading NCC table\n");
	 }
	 grTexDownloadTable(GR_TEXTABLE_NCC0, &(ti->palette));
      }
#endif

      grTexClampMode(GR_TMU0, ti->sClamp, ti->tClamp);
      grTexClampMode(GR_TMU1, ti->sClamp, ti->tClamp);
      grTexFilterMode(GR_TMU0, ti->minFilt, ti->maxFilt);
      grTexFilterMode(GR_TMU1, ti->minFilt, ti->maxFilt);
      grTexMipMapMode(GR_TMU0, ti->mmMode, ti->LODblend);
      grTexMipMapMode(GR_TMU1, ti->mmMode, ti->LODblend);

      grTexSource(GR_TMU0, ti->tm[FX_TMU0]->startAddr,
			    GR_MIPMAPLEVELMASK_ODD, &(ti->info));
      grTexSource(GR_TMU1, ti->tm[FX_TMU1]->startAddr,
			    GR_MIPMAPLEVELMASK_EVEN, &(ti->info));
   }
   else {
      if (ti->whichTMU == FX_TMU_BOTH)
	 tmu = FX_TMU0;
      else
	 tmu = ti->whichTMU;

      /* pointcast */
      if ((ti->info.format == GR_TEXFMT_P_8)
	  && (!fxMesa->haveGlobalPaletteTexture)) {
	 if (TDFX_DEBUG & VERBOSE_DRIVER) {
	    fprintf(stderr, "fxSetupSingleTMU_NoLock: uploading texture palette\n");
	 }
	 fxMesa->Glide.grTexDownloadTableExt(tmu, ti->paltype, &(ti->palette));
      }
#if FX_TC_NCC
      if ((ti->info.format == GR_TEXFMT_AYIQ_8422) ||
          (ti->info.format == GR_TEXFMT_YIQ_422)) {
	 if (TDFX_DEBUG & VERBOSE_DRIVER) {
	    fprintf(stderr, "fxSetupSingleTMU_NoLock: uploading NCC table\n");
	 }
	 fxMesa->Glide.grTexDownloadTableExt(tmu, GR_TEXTABLE_NCC0, &(ti->palette));
      }
#endif

      /* KW: The alternative is to do the download to the other tmu.  If
       * we get to this point, I think it means we are thrashing the
       * texture memory, so perhaps it's not a good idea.  
       */
      if (ti->LODblend && (TDFX_DEBUG & VERBOSE_DRIVER)) {
	 fprintf(stderr, "fxSetupSingleTMU_NoLock: not blending texture - only one tmu\n");
      }

      grTexClampMode(tmu, ti->sClamp, ti->tClamp);
      grTexFilterMode(tmu, ti->minFilt, ti->maxFilt);
      grTexMipMapMode(tmu, ti->mmMode, FXFALSE);

      grTexSource(tmu, ti->tm[tmu]->startAddr, GR_MIPMAPLEVELMASK_BOTH, &(ti->info));
   }
}

static void
fxSelectSingleTMUSrc_NoLock(fxMesaContext fxMesa, GLint tmu, FxBool LODblend)
{
   struct tdfx_texcombine tex0, tex1;

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "fxSelectSingleTMUSrc_NoLock(%d, %d)\n", tmu, LODblend);
   }

   tex0.InvertRGB     = FXFALSE;
   tex0.InvertAlpha   = FXFALSE;
   tex1.InvertRGB     = FXFALSE;
   tex1.InvertAlpha   = FXFALSE;

   if (LODblend) {
      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) {
         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 {
         tex1.FunctionRGB   = GR_COMBINE_FUNCTION_LOCAL;
         tex1.FactorRGB     = GR_COMBINE_FACTOR_NONE;
         tex1.FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         tex1.FactorAlpha   = GR_COMBINE_FACTOR_NONE;

	 /* [dBorca] Hack alert:
          * don't use GR_COMBINE_FUNCTION_SCALE_OTHER
          * such that Glide recognizes 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]._Current;
   int tmu;

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "fxSetupTextureSingleTMU_NoLock(%d)\n", textureset);
   }

   ti = fxTMGetTexInfo(tObj);

   fxTexValidate(ctx, tObj);

   fxSetupSingleTMU_NoLock(fxMesa, tObj);

   if (ti->whichTMU == FX_TMU_BOTH)
      tmu = FX_TMU0;
   else
      tmu = ti->whichTMU;
   if (fxMesa->tmuSrc != tmu)
      fxSelectSingleTMUSrc_NoLock(fxMesa, tmu, ti->LODblend);

   if (textureset == 0 || !fxMesa->haveTwoTMUs)
      unitsmode = fxGetTexSetConfiguration(ctx, tObj, NULL);
   else
      unitsmode = fxGetTexSetConfiguration(ctx, NULL, tObj);

/*    if(fxMesa->lastUnitsMode==unitsmode) */
/*      return; */

   fxMesa->lastUnitsMode = unitsmode;

   fxMesa->stw_hint_state = 0;
   FX_grHints_NoLock(GR_HINT_STWHINT, 0);

   ifmt = ti->baseLevelInternalFormat;

   if (unitsmode & FX_UM_ALPHA_ITERATED)
      locala = GR_COMBINE_LOCAL_ITERATED;
   else
      locala = GR_COMBINE_LOCAL_CONSTANT;

   if (unitsmode & FX_UM_COLOR_ITERATED)
      localc = GR_COMBINE_LOCAL_ITERATED;
   else
      localc = GR_COMBINE_LOCAL_CONSTANT;

   if (TDFX_DEBUG & (VERBOSE_DRIVER | VERBOSE_TEXTURE))
      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:
      alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
      alphaComb.Factor   = GR_COMBINE_FACTOR_NONE;
      alphaComb.Other    = GR_COMBINE_OTHER_NONE;

      colorComb.Function = GR_COMBINE_FUNCTION_BLEND;
      colorComb.Factor   = GR_COMBINE_FACTOR_TEXTURE_ALPHA;
      colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
      break;
   case GL_MODULATE:
      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 (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 {
         /* [dBorca] Hack alert:
          * only Voodoo^2 can GL_BLEND (GR_COMBINE_FACTOR_TEXTURE_RGB)
          */
         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 {
            _mesa_problem(NULL, "can't GL_BLEND with SST1");
            return;
         }
      }

      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)) {
         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, "fxSetupTextureSingleTMU_NoLock: %x Texture.EnvMode not yet supported\n",
		 ctx->Texture.Unit[textureset].EnvMode);
      }
      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
static void
fxSetupTextureSingleTMU(GLcontext * ctx, GLuint textureset)
{
   BEGIN_BOARD_LOCK();
   fxSetupTextureSingleTMU_NoLock(ctx, textureset);
   END_BOARD_LOCK();
}
#endif


/************************* Double Texture Set ***************************/

static void
fxSetupDoubleTMU_NoLock(fxMesaContext fxMesa,
			struct gl_texture_object *tObj0,
			struct gl_texture_object *tObj1)
{
#define T0_NOT_IN_TMU  0x01
#define T1_NOT_IN_TMU  0x02
#define T0_IN_TMU0     0x04
#define T1_IN_TMU0     0x08
#define T0_IN_TMU1     0x10
#define T1_IN_TMU1     0x20

   tfxTexInfo *ti0 = fxTMGetTexInfo(tObj0);
   tfxTexInfo *ti1 = fxTMGetTexInfo(tObj1);
   GLuint tstate = 0;
   int tmu0 = 0, tmu1 = 1;

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "fxSetupDoubleTMU_NoLock(...)\n");
   }

   /* We shouldn't need to do this. There is something wrong with
      mutlitexturing when the TMUs are swapped. So, we're forcing
      them to always be loaded correctly. !!! */
   if (ti0->whichTMU == FX_TMU1)
      fxTMMoveOutTM_NoLock(fxMesa, tObj0);
   if (ti1->whichTMU == FX_TMU0)
      fxTMMoveOutTM_NoLock(fxMesa, tObj1);

   if (ti0->isInTM) {
      switch (ti0->whichTMU) {
      case FX_TMU0:
	 tstate |= T0_IN_TMU0;
	 break;
      case FX_TMU1:
	 tstate |= T0_IN_TMU1;
	 break;
      case FX_TMU_BOTH:
	 tstate |= T0_IN_TMU0 | T0_IN_TMU1;
	 break;
      case FX_TMU_SPLIT:
	 tstate |= T0_NOT_IN_TMU;
	 break;
      }
   }
   else
      tstate |= T0_NOT_IN_TMU;

   if (ti1->isInTM) {
      switch (ti1->whichTMU) {
      case FX_TMU0:
	 tstate |= T1_IN_TMU0;
	 break;
      case FX_TMU1:
	 tstate |= T1_IN_TMU1;
	 break;
      case FX_TMU_BOTH:
	 tstate |= T1_IN_TMU0 | T1_IN_TMU1;
	 break;
      case FX_TMU_SPLIT:
	 tstate |= T1_NOT_IN_TMU;
	 break;
      }
   }
   else
      tstate |= T1_NOT_IN_TMU;

   ti0->lastTimeUsed = fxMesa->texBindNumber;
   ti1->lastTimeUsed = fxMesa->texBindNumber;

   /* Move texture maps into TMUs */

   if (!(((tstate & T0_IN_TMU0) && (tstate & T1_IN_TMU1)) ||
	 ((tstate & T0_IN_TMU1) && (tstate & T1_IN_TMU0)))) {
      if (tObj0 == tObj1)
	 fxTMMoveInTM_NoLock(fxMesa, tObj1, FX_TMU_BOTH);
      else {
	 /* Find the minimal way to correct the situation */
	 if ((tstate & T0_IN_TMU0) || (tstate & T1_IN_TMU1)) {
	    /* We have one in the standard order, setup the other */
	    if (tstate & T0_IN_TMU0) {	/* T0 is in TMU0, put T1 in TMU1 */
	       fxTMMoveInTM_NoLock(fxMesa, tObj1, FX_TMU1);
	    }
	    else {
	       fxTMMoveInTM_NoLock(fxMesa, tObj0, FX_TMU0);
	    }
	    /* tmu0 and tmu1 are setup */
	 }
	 else if ((tstate & T0_IN_TMU1) || (tstate & T1_IN_TMU0)) {
	    /* we have one in the reverse order, setup the other */
	    if (tstate & T1_IN_TMU0) {	/* T1 is in TMU0, put T0 in TMU1 */
	       fxTMMoveInTM_NoLock(fxMesa, tObj0, FX_TMU1);
	    }
	    else {
	       fxTMMoveInTM_NoLock(fxMesa, tObj1, FX_TMU0);
	    }
	    tmu0 = 1;
	    tmu1 = 0;
	 }
	 else {			/* Nothing is loaded */
	    fxTMMoveInTM_NoLock(fxMesa, tObj0, FX_TMU0);
	    fxTMMoveInTM_NoLock(fxMesa, tObj1, FX_TMU1);
	    /* tmu0 and tmu1 are setup */
	 }
      }
   }

   /* [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) {
      /* 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, "fxSetupDoubleTMU_NoLock: uploading texture palette for TMU0\n");
	 }
	 fxMesa->Glide.grTexDownloadTableExt(ti0->whichTMU, ti0->paltype, &(ti0->palette));
      }
   }
#if FX_TC_NCC
   /* pointcast */
   if ((ti1->info.format == GR_TEXFMT_AYIQ_8422) ||
       (ti1->info.format == GR_TEXFMT_YIQ_422)) {
      if (TDFX_DEBUG & VERBOSE_DRIVER) {
         fprintf(stderr, "fxSetupDoubleTMU_NoLock: uploading NCC0 table for TMU1\n");
      }
      fxMesa->Glide.grTexDownloadTableExt(ti1->whichTMU, GR_TEXTABLE_NCC0, &(ti1->palette));
   }
   if ((ti0->info.format == GR_TEXFMT_AYIQ_8422) ||
       (ti0->info.format == GR_TEXFMT_YIQ_422)) {
      if (TDFX_DEBUG & VERBOSE_DRIVER) {
         fprintf(stderr, "fxSetupDoubleTMU_NoLock: uploading NCC0 table for TMU0\n");
      }
      fxMesa->Glide.grTexDownloadTableExt(ti0->whichTMU, GR_TEXTABLE_NCC0, &(ti0->palette));
   }
#endif

   grTexSource(tmu0, ti0->tm[tmu0]->startAddr,
			 GR_MIPMAPLEVELMASK_BOTH, &(ti0->info));
   grTexClampMode(tmu0, ti0->sClamp, ti0->tClamp);
   grTexFilterMode(tmu0, ti0->minFilt, ti0->maxFilt);
   grTexMipMapMode(tmu0, ti0->mmMode, FXFALSE);

   grTexSource(tmu1, ti1->tm[tmu1]->startAddr,
			 GR_MIPMAPLEVELMASK_BOTH, &(ti1->info));
   grTexClampMode(tmu1, ti1->sClamp, ti1->tClamp);
   grTexFilterMode(tmu1, ti1->minFilt, ti1->maxFilt);
   grTexMipMapMode(tmu1, ti1->mmMode, FXFALSE);

#undef T0_NOT_IN_TMU
#undef T1_NOT_IN_TMU
#undef T0_IN_TMU0
#undef T1_IN_TMU0
#undef T0_IN_TMU1
#undef T1_IN_TMU1
}

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[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, "fxSetupTextureDoubleTMU_NoLock(...)\n");
   }

   ti0 = fxTMGetTexInfo(tObj0);
   fxTexValidate(ctx, tObj0);

   ti1 = fxTMGetTexInfo(tObj1);
   fxTexValidate(ctx, tObj1);

   fxSetupDoubleTMU_NoLock(fxMesa, tObj0, tObj1);

   unitsmode = fxGetTexSetConfiguration(ctx, tObj0, tObj1);

/*    if(fxMesa->lastUnitsMode==unitsmode) */
/*      return; */

   fxMesa->lastUnitsMode = unitsmode;

   fxMesa->stw_hint_state |= GR_STWHINT_ST_DIFF_TMU1;
   FX_grHints_NoLock(GR_HINT_STWHINT, fxMesa->stw_hint_state);

   envmode = unitsmode & FX_UM_E_ENVMODE;
   ifmt = unitsmode & FX_UM_E_IFMT;

   if (unitsmode & FX_UM_ALPHA_ITERATED)
      locala = GR_COMBINE_LOCAL_ITERATED;
   else
      locala = GR_COMBINE_LOCAL_CONSTANT;

   if (unitsmode & FX_UM_COLOR_ITERATED)
      localc = GR_COMBINE_LOCAL_ITERATED;
   else
      localc = GR_COMBINE_LOCAL_CONSTANT;


   if (TDFX_DEBUG & (VERBOSE_DRIVER | VERBOSE_TEXTURE))
      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));


   if ((ti0->whichTMU == FX_TMU1) || (ti1->whichTMU == FX_TMU0)) {
      tmu0 = 1;
      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):
      {
	 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_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 (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 {
         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) {
         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 {
         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;
      }

      colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
      colorComb.Factor   = GR_COMBINE_FACTOR_ONE;
      colorComb.Other    = GR_COMBINE_OTHER_TEXTURE;
      break;


   case (FX_UM_E0_MODULATE | FX_UM_E1_ADD):	/* Quake 3 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_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;
      }
   default:
      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 ***************************/

static void
fxSetupTextureNone_NoLock(GLcontext * ctx)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   GrCombineLocal_t localc, locala;

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "fxSetupTextureNone_NoLock(...)\n");
   }

   if ((ctx->Light.ShadeModel == GL_SMOOTH) || 1 ||
       (ctx->Point.SmoothFlag) ||
       (ctx->Line.SmoothFlag) ||
       (ctx->Polygon.SmoothFlag)) locala = GR_COMBINE_LOCAL_ITERATED;
   else
      locala = GR_COMBINE_LOCAL_CONSTANT;

   if (ctx->Light.ShadeModel == GL_SMOOTH || 1)
      localc = GR_COMBINE_LOCAL_ITERATED;
   else
      localc = GR_COMBINE_LOCAL_CONSTANT;

   grAlphaCombine(GR_COMBINE_FUNCTION_LOCAL,
                  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);

   fxMesa->lastUnitsMode = FX_UM_NONE;
}

#include "fxsetup.h"

/************************************************************************/
/************************** Texture Mode SetUp **************************/
/************************************************************************/

static void
fxSetupTexture_NoLock(GLcontext * ctx)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "fxSetupTexture_NoLock(...)\n");
   }

   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);
      }
   }
}

void
fxSetupTexture(GLcontext * ctx)
{
   BEGIN_BOARD_LOCK();
   fxSetupTexture_NoLock(ctx);
   END_BOARD_LOCK();
}

/************************************************************************/
/**************************** Blend SetUp *******************************/
/************************************************************************/

void
fxDDBlendFuncSeparate(GLcontext * ctx, GLenum sfactor, GLenum dfactor, GLenum asfactor, GLenum adfactor)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;
   GLboolean have32bpp = (fxMesa->colDepth == 32);
   GLboolean haveAlpha = fxMesa->haveHwAlpha;
   GrAlphaBlendFnc_t sfact, dfact, asfact, adfact;

   /* [dBorca] Hack alert:
    * We should condition *DST_ALPHA* modes
    * by the boolean `haveAlpha' above!
    * It indicates whether we really have HW alpha buffer...
    */

/*
When the value A_COLOR is selected as the destination alpha blending factor,
the source pixel color is used as the destination blending factor.  When the
value A_COLOR is selected as the source alpha blending factor, the destination
pixel color is used as the source blending factor.  When the value A_SAMECOLOR
is selected as the destination alpha blending factor, the destination pixel
color is used as the destination blending factor.  When the value A_SAMECOLOR
is selected as the source alpha blending factor, the source pixel color is
used as the source blending factor.  Note also that the alpha blending
function 0xf (A_COLORBEFOREFOG/ASATURATE) is different depending upon whether
it is being used as a source or destination alpha blending function.  When the
value 0xf is selected as the destination alpha blending factor, the source
color before the fog unit ("unfogged" color) is used as the destination
blending factor -- this alpha blending function is useful for multi-pass
rendering with atmospheric effects.  When the value 0xf is selected as the
source alpha blending factor, the alpha-saturate anti-aliasing algorithm is
selected -- this MIN function performs polygonal anti-aliasing for polygons
which are drawn front-to-back.

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
*/

   switch (sfactor) {
   case GL_ZERO:
      sfact = GR_BLEND_ZERO;
      break;
   case GL_ONE:
      sfact = GR_BLEND_ONE;
      break;
   case GL_DST_COLOR:
      sfact = GR_BLEND_DST_COLOR;
      break;
   case GL_ONE_MINUS_DST_COLOR:
      sfact = GR_BLEND_ONE_MINUS_DST_COLOR;
      break;
   case GL_SRC_ALPHA:
      sfact = GR_BLEND_SRC_ALPHA;
      break;
   case GL_ONE_MINUS_SRC_ALPHA:
      sfact = GR_BLEND_ONE_MINUS_SRC_ALPHA;
      break;
   case GL_DST_ALPHA:
      sfact = GR_BLEND_DST_ALPHA;
      break;
   case GL_ONE_MINUS_DST_ALPHA:
      sfact = GR_BLEND_ONE_MINUS_DST_ALPHA;
      break;
   case GL_SRC_ALPHA_SATURATE:
      sfact = GR_BLEND_ALPHA_SATURATE;
      break;
   case GL_SRC_COLOR:
   case GL_ONE_MINUS_SRC_COLOR:
      /* USELESS */
      sfact = GR_BLEND_ONE;
      break;
   default:
      sfact = GR_BLEND_ONE;
      break;
   }

   switch (asfactor) {
   case GL_ZERO:
      asfact = GR_BLEND_ZERO;
      break;
   case GL_ONE:
      asfact = GR_BLEND_ONE;
      break;
   case GL_DST_COLOR:
      asfact = GR_BLEND_ONE/*bad*/;
      break;
   case GL_ONE_MINUS_DST_COLOR:
      asfact = GR_BLEND_ONE/*bad*/;
      break;
   case GL_SRC_ALPHA:
      asfact = have32bpp ? GR_BLEND_SRC_ALPHA : GR_BLEND_ONE/*bad*/;
      break;
   case GL_ONE_MINUS_SRC_ALPHA:
      asfact = have32bpp ? GR_BLEND_ONE_MINUS_SRC_ALPHA : GR_BLEND_ONE/*bad*/;
      break;
   case GL_DST_ALPHA:
      asfact = have32bpp ? GR_BLEND_DST_ALPHA : GR_BLEND_ONE/*bad*/;
      break;
   case GL_ONE_MINUS_DST_ALPHA:
      asfact = have32bpp ? GR_BLEND_ONE_MINUS_DST_ALPHA : GR_BLEND_ONE/*bad*/;
      break;
   case GL_SRC_ALPHA_SATURATE:
      asfact = GR_BLEND_ONE/*bad*/;
      break;
   case GL_SRC_COLOR:
   case GL_ONE_MINUS_SRC_COLOR:
      /* USELESS */
      asfact = GR_BLEND_ONE/*bad*/;
      break;
   default:
      asfact = GR_BLEND_ONE/*bad*/;
      break;
   }

   switch (dfactor) {
   case GL_ZERO:
      dfact = GR_BLEND_ZERO;
      break;
   case GL_ONE:
      dfact = GR_BLEND_ONE;
      break;
   case GL_SRC_COLOR:
      dfact = GR_BLEND_SRC_COLOR;
      break;
   case GL_ONE_MINUS_SRC_COLOR:
      dfact = GR_BLEND_ONE_MINUS_SRC_COLOR;
      break;
   case GL_SRC_ALPHA:
      dfact = GR_BLEND_SRC_ALPHA;
      break;
   case GL_ONE_MINUS_SRC_ALPHA:
      dfact = GR_BLEND_ONE_MINUS_SRC_ALPHA;
      break;
   case GL_DST_ALPHA:
      /* dfact=GR_BLEND_DST_ALPHA; */
      /* We can't do DST_ALPHA */
      dfact = GR_BLEND_ONE;
      break;
   case GL_ONE_MINUS_DST_ALPHA:
      /* dfact=GR_BLEND_ONE_MINUS_DST_ALPHA; */
      /* We can't do DST_ALPHA */
      dfact = GR_BLEND_ZERO;
      break;
   case GL_SRC_ALPHA_SATURATE:
   case GL_DST_COLOR:
   case GL_ONE_MINUS_DST_COLOR:
      /* USELESS */
      dfact = GR_BLEND_ZERO;
      break;
   default:
      dfact = GR_BLEND_ZERO;
      break;
   }

   switch (adfactor) {
   case GL_ZERO:
      adfact = GR_BLEND_ZERO;
      break;
   case GL_ONE:
      adfact = GR_BLEND_ONE;
      break;
   case GL_SRC_COLOR:
      adfact = GR_BLEND_ZERO/*bad*/;
      break;
   case GL_ONE_MINUS_SRC_COLOR:
      adfact = GR_BLEND_ZERO/*bad*/;
      break;
   case GL_SRC_ALPHA:
      adfact = have32bpp ? GR_BLEND_SRC_ALPHA : GR_BLEND_ZERO/*bad*/;
      break;
   case GL_ONE_MINUS_SRC_ALPHA:
      adfact = have32bpp ? GR_BLEND_ONE_MINUS_SRC_ALPHA : GR_BLEND_ZERO/*bad*/;
      break;
   case GL_DST_ALPHA:
      adfact = have32bpp ? GR_BLEND_DST_ALPHA : GR_BLEND_ZERO/*bad*/;
      break;
   case GL_ONE_MINUS_DST_ALPHA:
      adfact = have32bpp ? GR_BLEND_ONE_MINUS_DST_ALPHA : GR_BLEND_ZERO/*bad*/;
      break;
   case GL_SRC_ALPHA_SATURATE:
   case GL_DST_COLOR:
   case GL_ONE_MINUS_DST_COLOR:
      /* USELESS */
      adfact = GR_BLEND_ZERO/*bad*/;
      break;
   default:
      adfact = GR_BLEND_ZERO/*bad*/;
      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;
      fxMesa->new_state |= FX_NEW_BLEND;
   }
}

void
fxDDBlendEquationSeparate(GLcontext * ctx, GLenum modeRGB, GLenum modeA)
{
 fxMesaContext fxMesa = FX_CONTEXT(ctx);
 tfxUnitsState *us = &fxMesa->unitsState;
 GrAlphaBlendOp_t q;

 assert( modeRGB == modeA );
 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:
             return;
 }

 if ((q != us->blendEq) && fxMesa->HavePixExt) {
    us->blendEq = q;
    fxMesa->new_state |= FX_NEW_BLEND;
 }
}

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->blendEq,
                                             us->blendSrcFuncAlpha, us->blendDstFuncAlpha,
                                             us->blendEq);
    } 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);
    }
 }
}

/************************************************************************/
/************************** Alpha Test SetUp ****************************/
/************************************************************************/

void
fxDDAlphaFunc(GLcontext * ctx, GLenum func, GLfloat ref)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;

   if (
       (us->alphaTestFunc != func)
       ||
       (us->alphaTestRefValue != ref)
      ) {
      us->alphaTestFunc = func;
      us->alphaTestRefValue = ref;
      fxMesa->new_state |= FX_NEW_ALPHA;
   }
}

static void
fxSetupAlphaTest(GLcontext * ctx)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;

   if (us->alphaTestEnabled) {
      GrAlpha_t ref = (GLint) (us->alphaTestRefValue * 255.0);
      grAlphaTestFunction(us->alphaTestFunc - GL_NEVER + GR_CMP_NEVER);
      grAlphaTestReferenceValue(ref);
   }
   else
      grAlphaTestFunction(GR_CMP_ALWAYS);
}

/************************************************************************/
/************************** Depth Test SetUp ****************************/
/************************************************************************/

void
fxDDDepthFunc(GLcontext * ctx, GLenum func)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;

   if (us->depthTestFunc != func) {
      us->depthTestFunc = func;
      fxMesa->new_state |= FX_NEW_DEPTH;
   }
}

void
fxDDDepthMask(GLcontext * ctx, GLboolean flag)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;

   if (flag != us->depthMask) {
      us->depthMask = flag;
      fxMesa->new_state |= FX_NEW_DEPTH;
   }
}

void
fxSetupDepthTest(GLcontext * ctx)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;

   if (us->depthTestEnabled) {
      grDepthBufferFunction(us->depthTestFunc - GL_NEVER + GR_CMP_NEVER);
      grDepthMask(us->depthMask);
   }
   else {
      grDepthBufferFunction(GR_CMP_ALWAYS);
      grDepthMask(FXFALSE);
   }
}

/************************************************************************/
/************************** Stencil SetUp *******************************/
/************************************************************************/

static GrStencil_t convertGLStencilOp( GLenum op )
{
   switch ( op ) {
   case GL_KEEP:
      return GR_STENCILOP_KEEP;
   case GL_ZERO:
      return GR_STENCILOP_ZERO;
   case GL_REPLACE:
      return GR_STENCILOP_REPLACE;
   case GL_INCR:
      return GR_STENCILOP_INCR_CLAMP;
   case GL_DECR:
      return GR_STENCILOP_DECR_CLAMP;
   case GL_INVERT:
      return GR_STENCILOP_INVERT;
   case GL_INCR_WRAP_EXT:
      return GR_STENCILOP_INCR_WRAP;
   case GL_DECR_WRAP_EXT:
      return GR_STENCILOP_DECR_WRAP;
   default:
      _mesa_problem( NULL, "bad stencil op in convertGLStencilOp" );
   }
   return GR_STENCILOP_KEEP;   /* never get, silence compiler warning */
}

void
fxDDStencilFunc (GLcontext *ctx, GLenum func, GLint ref, GLuint mask)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;

   if (
       (us->stencilFunction != func)
       ||
       (us->stencilRefValue != ref)
       ||
       (us->stencilValueMask != mask)
      ) {
      us->stencilFunction = func;
      us->stencilRefValue = ref;
      us->stencilValueMask = mask;
      fxMesa->new_state |= FX_NEW_STENCIL;
   }
}

void
fxDDStencilMask (GLcontext *ctx, GLuint mask)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;

   if (us->stencilWriteMask != mask) {
      us->stencilWriteMask = mask;
      fxMesa->new_state |= FX_NEW_STENCIL;
   }
}

void
fxDDStencilOp (GLcontext *ctx, GLenum sfail, GLenum zfail, GLenum zpass)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;

   if (
       (us->stencilFailFunc != sfail)
       ||
       (us->stencilZFailFunc != zfail)
       ||
       (us->stencilZPassFunc != zpass)
      ) {
      us->stencilFailFunc = sfail;
      us->stencilZFailFunc = zfail;
      us->stencilZPassFunc = zpass;
      fxMesa->new_state |= FX_NEW_STENCIL;
   }
}

static void
fxSetupStencil (GLcontext * ctx)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;

   if (us->stencilEnabled) {
      grEnable(GR_STENCIL_MODE_EXT);
      fxMesa->Glide.grStencilOpExt(convertGLStencilOp(us->stencilFailFunc),
                                   convertGLStencilOp(us->stencilZFailFunc),
                                   convertGLStencilOp(us->stencilZPassFunc));
      fxMesa->Glide.grStencilFuncExt(us->stencilFunction - GL_NEVER + GR_CMP_NEVER,
                                     us->stencilRefValue,
                                     us->stencilValueMask);
      fxMesa->Glide.grStencilMaskExt(us->stencilWriteMask);
   } else {
      grDisable(GR_STENCIL_MODE_EXT);
   }
}

/************************************************************************/
/**************************** Color Mask SetUp **************************/
/************************************************************************/

void
fxDDColorMask(GLcontext * ctx,
	      GLboolean r, GLboolean g, GLboolean b, GLboolean a)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   fxMesa->new_state |= FX_NEW_COLOR_MASK;
   (void) r;
   (void) g;
   (void) b;
   (void) a;
}

void
fxSetupColorMask(GLcontext * ctx)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);

   if (fxMesa->colDepth != 16) {
      /* 32bpp mode or 15bpp mode */
      fxMesa->Glide.grColorMaskExt(ctx->Color.ColorMask[RCOMP],
                                   ctx->Color.ColorMask[GCOMP],
                                   ctx->Color.ColorMask[BCOMP],
                                   ctx->Color.ColorMask[ACOMP] && fxMesa->haveHwAlpha);
   }
   else {
      /* 16 bpp mode */
      grColorMask(ctx->Color.ColorMask[RCOMP] |
                  ctx->Color.ColorMask[GCOMP] |
                  ctx->Color.ColorMask[BCOMP],
                  ctx->Color.ColorMask[ACOMP] && fxMesa->haveHwAlpha);
   }
}




/************************************************************************/
/**************************** Fog Mode SetUp ****************************/
/************************************************************************/

/*
 * This is called during state update in order to update the Glide fog state.
 */
static void
fxSetupFog(GLcontext * ctx)
{
   if (ctx->Fog.Enabled /*&& ctx->FogMode==FOG_FRAGMENT */ ) {
      fxMesaContext fxMesa = FX_CONTEXT(ctx);

      /* update fog color */
      GLubyte col[4];
      col[0] = (unsigned int) (255 * ctx->Fog.Color[0]);
      col[1] = (unsigned int) (255 * ctx->Fog.Color[1]);
      col[2] = (unsigned int) (255 * ctx->Fog.Color[2]);
      col[3] = (unsigned int) (255 * ctx->Fog.Color[3]);
      grFogColorValue(FXCOLOR4(col));

      if (fxMesa->fogTableMode != ctx->Fog.Mode ||
	  fxMesa->fogDensity != ctx->Fog.Density ||
	  fxMesa->fogStart != ctx->Fog.Start ||
	  fxMesa->fogEnd != ctx->Fog.End) {
	 /* reload the fog table */
	 switch (ctx->Fog.Mode) {
	 case GL_LINEAR:
	    guFogGenerateLinear(fxMesa->fogTable, ctx->Fog.Start,
				ctx->Fog.End);
	    break;
	 case GL_EXP:
	    guFogGenerateExp(fxMesa->fogTable, ctx->Fog.Density);
	    break;
	 case GL_EXP2:
	    guFogGenerateExp2(fxMesa->fogTable, ctx->Fog.Density);
	    break;
	 default:
	    ;
	 }
	 fxMesa->fogTableMode = ctx->Fog.Mode;
	 fxMesa->fogDensity = ctx->Fog.Density;
	 fxMesa->fogStart = ctx->Fog.Start;
	 fxMesa->fogEnd = ctx->Fog.End;
      }

      grFogTable(fxMesa->fogTable);
      grFogMode(GR_FOG_WITH_TABLE_ON_Q);
   }
   else {
      grFogMode(GR_FOG_DISABLE);
   }
}

void
fxDDFogfv(GLcontext * ctx, GLenum pname, const GLfloat * params)
{
   FX_CONTEXT(ctx)->new_state |= FX_NEW_FOG;
}

/************************************************************************/
/************************** Scissor Test SetUp **************************/
/************************************************************************/

/* This routine is used in managing the lock state, and therefore can't lock */
void
fxSetScissorValues(GLcontext * ctx)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   int xmin, xmax;
   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;

      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;
   }

   fxMesa->clipMinX = xmin;
   fxMesa->clipMinY = ymin;
   fxMesa->clipMaxX = xmax;
   fxMesa->clipMaxY = ymax;
   grClipWindow(xmin, ymin, xmax, ymax);
}

void
fxSetupScissor(GLcontext * ctx)
{
   BEGIN_BOARD_LOCK();
   fxSetScissorValues(ctx);
   END_BOARD_LOCK();
}

void
fxDDScissor(GLcontext * ctx, GLint x, GLint y, GLsizei w, GLsizei h)
{
   FX_CONTEXT(ctx)->new_state |= FX_NEW_SCISSOR;
}

/************************************************************************/
/*************************** Cull mode setup ****************************/
/************************************************************************/


void
fxDDCullFace(GLcontext * ctx, GLenum mode)
{
   (void) mode;
   FX_CONTEXT(ctx)->new_state |= FX_NEW_CULL;
}

void
fxDDFrontFace(GLcontext * ctx, GLenum mode)
{
   (void) mode;
   FX_CONTEXT(ctx)->new_state |= FX_NEW_CULL;
}


void
fxSetupCull(GLcontext * ctx)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   GrCullMode_t mode = GR_CULL_DISABLE;

   if (ctx->Polygon.CullFlag && (fxMesa->raster_primitive == GL_TRIANGLES)) {
      switch (ctx->Polygon.CullFaceMode) {
      case GL_BACK:
	 if (ctx->Polygon.FrontFace == GL_CCW)
	    mode = GR_CULL_NEGATIVE;
	 else
	    mode = GR_CULL_POSITIVE;
	 break;
      case GL_FRONT:
	 if (ctx->Polygon.FrontFace == GL_CCW)
	    mode = GR_CULL_POSITIVE;
	 else
	    mode = GR_CULL_NEGATIVE;
	 break;
      case GL_FRONT_AND_BACK:
	 /* Handled as a fallback on triangles in tdfx_tris.c */
	 return;
      default:
	 ASSERT(0);
	 break;
      }
   }

   if (fxMesa->cullMode != mode) {
      fxMesa->cullMode = mode;
      grCullMode(mode);
   }
}


/************************************************************************/
/****************************** DD Enable ******************************/
/************************************************************************/

void
fxDDEnable(GLcontext * ctx, GLenum cap, GLboolean state)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   tfxUnitsState *us = &fxMesa->unitsState;

   if (TDFX_DEBUG & VERBOSE_DRIVER) {
      fprintf(stderr, "%s(%s)\n", state ? "fxDDEnable" : "fxDDDisable",
	      _mesa_lookup_enum_by_nr(cap));
   }

   switch (cap) {
   case GL_ALPHA_TEST:
      if (state != us->alphaTestEnabled) {
	 us->alphaTestEnabled = state;
	 fxMesa->new_state |= FX_NEW_ALPHA;
      }
      break;
   case GL_BLEND:
      if (state != us->blendEnabled) {
	 us->blendEnabled = state;
	 fxMesa->new_state |= FX_NEW_BLEND;
      }
      break;
   case GL_DEPTH_TEST:
      if (state != us->depthTestEnabled) {
	 us->depthTestEnabled = state;
	 fxMesa->new_state |= FX_NEW_DEPTH;
      }
      break;
   case GL_STENCIL_TEST:
      if (fxMesa->haveHwStencil && state != us->stencilEnabled) {
	 us->stencilEnabled = state;
	 fxMesa->new_state |= FX_NEW_STENCIL;
      }
      break;
   case GL_DITHER:
      if (state) {
	 grDitherMode(GR_DITHER_4x4);
      }
      else {
	 grDitherMode(GR_DITHER_DISABLE);
      }
      break;
   case GL_SCISSOR_TEST:
      fxMesa->new_state |= FX_NEW_SCISSOR;
      break;
   case GL_SHARED_TEXTURE_PALETTE_EXT:
      fxDDTexUseGlbPalette(ctx, state);
      break;
   case GL_FOG:
      fxMesa->new_state |= FX_NEW_FOG;
      break;
   case GL_CULL_FACE:
      fxMesa->new_state |= FX_NEW_CULL;
      break;
   case GL_LINE_SMOOTH:
   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;
   default:
      ;				/* XXX no-op? */
   }
}




/************************************************************************/
/************************** Changes to units state **********************/
/************************************************************************/


/* All units setup is handled under texture setup.
 */
void
fxDDShadeModel(GLcontext * ctx, GLenum mode)
{
   FX_CONTEXT(ctx)->new_state |= FX_NEW_TEXTURING;
}



/************************************************************************/
/****************************** Units SetUp *****************************/
/************************************************************************/
static void
fx_print_state_flags(const char *msg, GLuint flags)
{
   fprintf(stderr,
	   "%s: (0x%x) %s%s%s%s%s%s%s%s\n",
	   msg,
	   flags,
	   (flags & FX_NEW_TEXTURING) ? "texture, " : "",
	   (flags & FX_NEW_BLEND) ? "blend, " : "",
	   (flags & FX_NEW_ALPHA) ? "alpha, " : "",
	   (flags & FX_NEW_FOG) ? "fog, " : "",
	   (flags & FX_NEW_SCISSOR) ? "scissor, " : "",
	   (flags & FX_NEW_COLOR_MASK) ? "colormask, " : "",
	   (flags & FX_NEW_CULL) ? "cull, " : "",
	   (flags & FX_NEW_STENCIL) ? "stencil, " : "");
}

void
fxSetupFXUnits(GLcontext * ctx)
{
   fxMesaContext fxMesa = FX_CONTEXT(ctx);
   GLuint newstate = fxMesa->new_state;

   if (TDFX_DEBUG & VERBOSE_DRIVER)
      fx_print_state_flags("fxSetupFXUnits", newstate);

   if (newstate) {
      if (newstate & FX_NEW_TEXTURING)
	 fxSetupTexture(ctx);

      if (newstate & FX_NEW_BLEND)
	 fxSetupBlend(ctx);

      if (newstate & FX_NEW_ALPHA)
	 fxSetupAlphaTest(ctx);

      if (newstate & FX_NEW_DEPTH)
	 fxSetupDepthTest(ctx);

      if (newstate & FX_NEW_STENCIL)
	 fxSetupStencil(ctx);

      if (newstate & FX_NEW_FOG)
	 fxSetupFog(ctx);

      if (newstate & FX_NEW_SCISSOR)
	 fxSetupScissor(ctx);

      if (newstate & FX_NEW_COLOR_MASK)
	 fxSetupColorMask(ctx);

      if (newstate & FX_NEW_CULL)
	 fxSetupCull(ctx);

      fxMesa->new_state = 0;
   }
}



#else


/*
 * Need this to provide at least one external definition.
 */

extern int gl_fx_dummy_function_setup(void);
int
gl_fx_dummy_function_setup(void)
{
   return 0;
}

#endif /* FX */
