/* -*- mode: C; tab-width:8; c-basic-offset:2 -*- */

/*
 * Mesa 3-D graphics library
 * Version:  3.1
 *
 * Copyright (C) 1999  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.
 *
 *
 * Original Mesa / 3Dfx device driver (C) 1999 David Bucciarelli, by the
 * terms stated above.
 *
 * Thank you for your contribution, David!
 *
 * Please make note of the above copyright/license statement.  If you
 * contributed code or bug fixes to this code under the previous (GNU
 * Library) license and object to the new license, your code will be
 * removed at your request.  Please see the Mesa docs/COPYRIGHT file
 * for more information.
 *
 * Additional Mesa/3Dfx driver developers:
 *   Daryll Strauss <daryll@precisioninsight.com>
 *   Keith Whitwell <keith@precisioninsight.com>
 *
 * See fxapi.h for more revision/author details.
 */


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

static GLuint fxGetTexSetConfiguration(GLcontext *ctx,
				struct gl_texture_object *tObj0,
				struct gl_texture_object *tObj1);
static void fxSetupTextureSingleTMU_NoLock(GLcontext *ctx, GLuint textureset);
static void fxSetupDoubleTMU_NoLock(fxMesaContext fxMesa, 
			     struct gl_texture_object *tObj0,
			     struct gl_texture_object *tObj1);
static void fxSetupTexture_NoLock(GLcontext *ctx);
static void fxSetupTexture(GLcontext *ctx);
static void fxSetupBlend(GLcontext *ctx);
static void fxSetupDepthTest(GLcontext *ctx);
static void fxSetupScissor(GLcontext *ctx);
static void fxSetupCull(GLcontext *ctx);
static void gl_print_fx_state_flags( const char *msg, GLuint flags);
/*  static GLboolean fxMultipassBlend(struct vertex_buffer *, GLuint); */
static GLboolean fxMultipassTexture( struct vertex_buffer *, GLuint );

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

  if (MESA_VERBOSE&VERBOSE_DRIVER) {
     fprintf(stderr,"fxmesa: fxTexValidate(...) Start\n");
  }

  if(ti->validated) {
     if (MESA_VERBOSE&VERBOSE_DRIVER) {
	fprintf(stderr,"fxmesa: fxTexValidate(...) End (validated=GL_TRUE)\n");
     }
    return;
  }

  ti->tObj=tObj;
  minl=ti->minLevel=tObj->BaseLevel;
  maxl=ti->maxLevel=MIN2(tObj->MaxLevel,tObj->Image[0]->MaxLog2);

  fxTexGetInfo(tObj->Image[minl]->Width, tObj->Image[minl]->Height,
	       &(FX_largeLodLog2(ti->info)), &(FX_aspectRatioLog2(ti->info)),
	       &(ti->sScale), &(ti->tScale),
	       &(ti->int_sScale), &(ti->int_tScale),	       
	       NULL, NULL);

  if((tObj->MinFilter!=GL_NEAREST) && (tObj->MinFilter!=GL_LINEAR))
    fxTexGetInfo(tObj->Image[maxl]->Width,tObj->Image[maxl]->Height,
		 &(FX_smallLodLog2(ti->info)),NULL,
		 NULL,NULL,
		 NULL,NULL,
		 NULL,NULL);
  else
    FX_smallLodLog2(ti->info)=FX_largeLodLog2(ti->info);

  fxTexGetFormat(tObj->Image[minl]->Format,&(ti->info.format),&(ti->baseLevelInternalFormat));

  switch (tObj->WrapS) {
  case GL_CLAMP_TO_EDGE:
    /* What's this really mean compared to GL_CLAMP? */
  case GL_CLAMP:
    ti->sClamp=1;
    break;
  case GL_REPEAT:
    ti->sClamp=0;
    break;
  default:
    ;  /* silence compiler warning */
  }
  switch (tObj->WrapT) {
  case GL_CLAMP_TO_EDGE:
    /* What's this really mean compared to GL_CLAMP? */
  case GL_CLAMP:
    ti->tClamp=1;
    break;
  case GL_REPEAT:
    ti->tClamp=0;
    break;
  default:
    ;  /* silence compiler warning */
  }

  ti->validated=GL_TRUE;

  ti->info.data=NULL;

  if (MESA_VERBOSE&VERBOSE_DRIVER) {
     fprintf(stderr,"fxmesa: fxTexValidate(...) End\n");
  }
}

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 (MESA_VERBOSE & (VERBOSE_DRIVER|VERBOSE_TEXTURE)) 
     fxPrintUnitsMode("unitsmode", 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;

  /* 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 (fxMesa->freeTexMem[FX_TMU0] >
	    FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_BOTH,
					      &(ti->info))) {
	  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) {
    if ((ti->info.format==GR_TEXFMT_P_8) && (!fxMesa->haveGlobalPaletteTexture)) {
       if (MESA_VERBOSE&VERBOSE_DRIVER) {
	  fprintf(stderr,"fxmesa: uploading texture palette\n");
       }
      FX_grTexDownloadTable_NoLock(GR_TMU0,GR_TEXTABLE_PALETTE,&(ti->palette));
      FX_grTexDownloadTable_NoLock(GR_TMU1,GR_TEXTABLE_PALETTE,&(ti->palette));
    }

    FX_grTexClampMode_NoLock(GR_TMU0,ti->sClamp,ti->tClamp);
    FX_grTexClampMode_NoLock(GR_TMU1,ti->sClamp,ti->tClamp);
    FX_grTexFilterMode_NoLock(GR_TMU0,ti->minFilt,ti->maxFilt);
    FX_grTexFilterMode_NoLock(GR_TMU1,ti->minFilt,ti->maxFilt);
    FX_grTexMipMapMode_NoLock(GR_TMU0,ti->mmMode,ti->LODblend);
    FX_grTexMipMapMode_NoLock(GR_TMU1,ti->mmMode,ti->LODblend);

    FX_grTexSource_NoLock(GR_TMU0,ti->tm[FX_TMU0]->startAddr,
			  GR_MIPMAPLEVELMASK_ODD,&(ti->info));
    FX_grTexSource_NoLock(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;

    if((ti->info.format==GR_TEXFMT_P_8) && (!fxMesa->haveGlobalPaletteTexture)) {
       if (MESA_VERBOSE&VERBOSE_DRIVER) {
	  fprintf(stderr,"fxmesa: uploading texture palette\n");
       }
      FX_grTexDownloadTable_NoLock(tmu, GR_TEXTABLE_PALETTE, &(ti->palette));
    }

    /* 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 && (MESA_VERBOSE&VERBOSE_DRIVER))
       fprintf(stderr, "fxmesa: not blending texture - only on one tmu\n");

    FX_grTexClampMode_NoLock(tmu, ti->sClamp, ti->tClamp);
    FX_grTexFilterMode_NoLock(tmu, ti->minFilt, ti->maxFilt);
    FX_grTexMipMapMode_NoLock(tmu, ti->mmMode, FXFALSE);

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

static void fxSelectSingleTMUSrc_NoLock(fxMesaContext fxMesa, GLint tmu, 
					FxBool LODblend)
{
   if (MESA_VERBOSE&VERBOSE_DRIVER) {
      fprintf(stderr,"fxmesa: fxSelectSingleTMUSrc(%d,%d)\n",tmu,LODblend);
   }

  if(LODblend) {
    FX_grTexCombine_NoLock(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);

    FX_grTexCombine_NoLock(GR_TMU1,
			   GR_COMBINE_FUNCTION_LOCAL,GR_COMBINE_FACTOR_NONE,
			   GR_COMBINE_FUNCTION_LOCAL,GR_COMBINE_FACTOR_NONE,
			   FXFALSE,FXFALSE);
    fxMesa->tmuSrc=FX_TMU_SPLIT;
  } else {
    if (tmu!=FX_TMU1) {
      FX_grTexCombine_NoLock(GR_TMU0,
			     GR_COMBINE_FUNCTION_LOCAL,GR_COMBINE_FACTOR_NONE,
			     GR_COMBINE_FUNCTION_LOCAL,GR_COMBINE_FACTOR_NONE,
			     FXFALSE,FXFALSE);
      FX_grTexCombine_NoLock(GR_TMU1,
			     GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE,
			     GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE,
			     FXFALSE,FXFALSE);
      fxMesa->tmuSrc=FX_TMU0;
    } else {
      FX_grTexCombine_NoLock(GR_TMU1,
			     GR_COMBINE_FUNCTION_LOCAL,GR_COMBINE_FACTOR_NONE,
			     GR_COMBINE_FUNCTION_LOCAL,GR_COMBINE_FACTOR_NONE,
			     FXFALSE,FXFALSE);
    
      /* GR_COMBINE_FUNCTION_SCALE_OTHER doesn't work ?!? */
    
      FX_grTexCombine_NoLock(GR_TMU0,
			     GR_COMBINE_FUNCTION_BLEND,
			     GR_COMBINE_FACTOR_ONE,
			     GR_COMBINE_FUNCTION_BLEND,
			     GR_COMBINE_FACTOR_ONE,
			     FXFALSE,FXFALSE);
    
      fxMesa->tmuSrc=FX_TMU1;
    }
  }
}

static void fxSetupTextureSingleTMU_NoLock(GLcontext *ctx, GLuint textureset)
{
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
  GrCombineLocal_t localc,locala;
  GLuint unitsmode;
  GLint ifmt;
  tfxTexInfo *ti;
  struct gl_texture_object *tObj=ctx->Texture.Unit[textureset].CurrentD[2];
  int tmu;

  if (MESA_VERBOSE&VERBOSE_DRIVER) {
     fprintf(stderr,"fxmesa: fxSetupTextureSingleTMU(...) Start\n");
  }

  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 (MESA_VERBOSE & (VERBOSE_DRIVER|VERBOSE_TEXTURE))
     fprintf(stderr, "fxMesa: fxSetupTextureSingleTMU, envmode is %s\n",
	     gl_lookup_enum_by_nr(ctx->Texture.Unit[textureset].EnvMode));

  switch(ctx->Texture.Unit[textureset].EnvMode) {
  case GL_DECAL:
    FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_LOCAL,
			     GR_COMBINE_FACTOR_NONE,
			     locala,
			     GR_COMBINE_OTHER_NONE,
			     FXFALSE);

    FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_BLEND,
			     GR_COMBINE_FACTOR_TEXTURE_ALPHA,
			     localc,
			     GR_COMBINE_OTHER_TEXTURE,
			     FXFALSE);
    break;
  case GL_MODULATE:
    FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			     GR_COMBINE_FACTOR_LOCAL,
			     locala,
			     GR_COMBINE_OTHER_TEXTURE,
			     FXFALSE);

    if(ifmt==GL_ALPHA)
      FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       localc,
			       GR_COMBINE_OTHER_NONE,
			       FXFALSE);
    else
      FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			       GR_COMBINE_FACTOR_LOCAL,
			       localc,
			       GR_COMBINE_OTHER_TEXTURE,
			       FXFALSE);
    break;
  case GL_BLEND:
#if 0
    FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			     GR_COMBINE_FACTOR_LOCAL,
			     locala,
			     GR_COMBINE_OTHER_TEXTURE,
			     FXFALSE);
    if (ifmt==GL_ALPHA)
      FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       localc,
			       GR_COMBINE_OTHER_NONE,
			       FXFALSE);
    else
      FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL,
			       GR_COMBINE_FACTOR_LOCAL,
			       localc,
			       GR_COMBINE_OTHER_TEXTURE,
			       FXTRUE);
    ctx->Driver.MultipassFunc = fxMultipassBlend;
#else
    if (MESA_VERBOSE&VERBOSE_DRIVER)
      fprintf(stderr,"fx Driver: GL_BLEND not yet supported\n");
#endif    
    break;
  case GL_REPLACE:
    if((ifmt==GL_RGB) || (ifmt==GL_LUMINANCE))
      FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       locala,
			       GR_COMBINE_OTHER_NONE,
			       FXFALSE);
    else
      FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			       GR_COMBINE_FACTOR_ONE,
			       locala,
			       GR_COMBINE_OTHER_TEXTURE,
			       FXFALSE);
    
    if(ifmt==GL_ALPHA)
      FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       localc,
			       GR_COMBINE_OTHER_NONE,
			       FXFALSE);
    else
      FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			       GR_COMBINE_FACTOR_ONE,
			       localc,
			       GR_COMBINE_OTHER_TEXTURE,
			       FXFALSE);
    break;
  default:
    if (MESA_VERBOSE&VERBOSE_DRIVER)
      fprintf(stderr, "fx Driver: %x Texture.EnvMode not yet supported\n",
	      ctx->Texture.Unit[textureset].EnvMode);
    break;
  }

  if (MESA_VERBOSE&VERBOSE_DRIVER) {
     fprintf(stderr,"fxmesa: fxSetupTextureSingleTMU(...) End\n");
  }
}

static void fxSetupTextureSingleTMU(GLcontext *ctx, GLuint textureset) {
  BEGIN_BOARD_LOCK();
  fxSetupTextureSingleTMU_NoLock(ctx, textureset);
  END_BOARD_LOCK();
}

/************************* 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 (MESA_VERBOSE&VERBOSE_DRIVER) {
     fprintf(stderr,"fxmesa: fxSetupDoubleTMU(...)\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 */
      }
    }
  }

  if (!fxMesa->haveGlobalPaletteTexture) {
    if (ti0->info.format==GR_TEXFMT_P_8) {
       if (MESA_VERBOSE&VERBOSE_DRIVER) {
	  fprintf(stderr,"fxmesa: uploading texture palette TMU0\n");
       }
       FX_grTexDownloadTable_NoLock(tmu0, GR_TEXTABLE_PALETTE, &(ti0->palette));
    }

    if (ti1->info.format==GR_TEXFMT_P_8) {
       if (MESA_VERBOSE&VERBOSE_DRIVER) {
	  fprintf(stderr,"fxmesa: uploading texture palette TMU1\n");
       }
       FX_grTexDownloadTable_NoLock(tmu1, GR_TEXTABLE_PALETTE, &(ti1->palette));
    }
  }

  FX_grTexSource_NoLock(tmu0, ti0->tm[tmu0]->startAddr, 
			GR_MIPMAPLEVELMASK_BOTH, &(ti0->info));
  FX_grTexClampMode_NoLock(tmu0, ti0->sClamp, ti0->tClamp);
  FX_grTexFilterMode_NoLock(tmu0, ti0->minFilt, ti0->maxFilt);
  FX_grTexMipMapMode_NoLock(tmu0, ti0->mmMode, FXFALSE);

  FX_grTexSource_NoLock(tmu1, ti1->tm[tmu1]->startAddr,
			GR_MIPMAPLEVELMASK_BOTH, &(ti1->info));
  FX_grTexClampMode_NoLock(tmu1, ti1->sClamp, ti1->tClamp);
  FX_grTexFilterMode_NoLock(tmu1, ti1->minFilt, ti1->maxFilt);
  FX_grTexMipMapMode_NoLock(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=(fxMesaContext)ctx->DriverCtx;
  GrCombineLocal_t localc,locala;
  tfxTexInfo *ti0,*ti1;
  struct gl_texture_object *tObj0=ctx->Texture.Unit[0].CurrentD[2];
  struct gl_texture_object *tObj1=ctx->Texture.Unit[1].CurrentD[2];
  GLuint envmode,ifmt,unitsmode;
  int tmu0=0, tmu1=1;

  if (MESA_VERBOSE&VERBOSE_DRIVER) {
     fprintf(stderr,"fxmesa: fxSetupTextureDoubleTMU(...) Start\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 (MESA_VERBOSE & (VERBOSE_DRIVER|VERBOSE_TEXTURE))
     fprintf(stderr, "fxMesa: fxSetupTextureDoubleTMU, envmode is %s/%s\n",
	     gl_lookup_enum_by_nr(ctx->Texture.Unit[0].EnvMode),
	     gl_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;
  switch(envmode) {
  case (FX_UM_E0_MODULATE | FX_UM_E1_MODULATE):
    {
      GLboolean isalpha[FX_NUM_TMU];

      if(ti0->baseLevelInternalFormat==GL_ALPHA)
	isalpha[tmu0]=GL_TRUE;
      else
	isalpha[tmu0]=GL_FALSE;

      if(ti1->baseLevelInternalFormat==GL_ALPHA)
	isalpha[tmu1]=GL_TRUE;
      else
	isalpha[tmu1]=GL_FALSE;
	
      if(isalpha[FX_TMU1])
	FX_grTexCombine_NoLock(GR_TMU1,
			       GR_COMBINE_FUNCTION_ZERO,
			       GR_COMBINE_FACTOR_NONE,
			       GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       FXTRUE,FXFALSE);
      else
	FX_grTexCombine_NoLock(GR_TMU1,
			       GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       FXFALSE,FXFALSE);

      if(isalpha[FX_TMU0])
	FX_grTexCombine_NoLock(GR_TMU0,
			       GR_COMBINE_FUNCTION_BLEND_OTHER,
			       GR_COMBINE_FACTOR_ONE,
			       GR_COMBINE_FUNCTION_BLEND_OTHER,
			       GR_COMBINE_FACTOR_LOCAL,
			       FXFALSE,FXFALSE);
      else
	FX_grTexCombine_NoLock(GR_TMU0,
			       GR_COMBINE_FUNCTION_BLEND_OTHER,
			       GR_COMBINE_FACTOR_LOCAL,
			       GR_COMBINE_FUNCTION_BLEND_OTHER,
			       GR_COMBINE_FACTOR_LOCAL,
			       FXFALSE,FXFALSE);

      FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			       GR_COMBINE_FACTOR_LOCAL,
			       localc,
			       GR_COMBINE_OTHER_TEXTURE,
			       FXFALSE);

      FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			       GR_COMBINE_FACTOR_LOCAL,
			       locala,
			       GR_COMBINE_OTHER_TEXTURE,
			       FXFALSE);
      break;
    }
  case (FX_UM_E0_REPLACE | FX_UM_E1_BLEND): /* Only for GLQuake */
    if (tmu1==FX_TMU1) {
      FX_grTexCombine_NoLock(GR_TMU1,
			     GR_COMBINE_FUNCTION_LOCAL,
			     GR_COMBINE_FACTOR_NONE,
			     GR_COMBINE_FUNCTION_LOCAL,
			     GR_COMBINE_FACTOR_NONE,
			     FXTRUE,FXFALSE);
		  
      FX_grTexCombine_NoLock(GR_TMU0,
			     GR_COMBINE_FUNCTION_BLEND_OTHER,
			     GR_COMBINE_FACTOR_LOCAL,
			     GR_COMBINE_FUNCTION_BLEND_OTHER,
			     GR_COMBINE_FACTOR_LOCAL,
			     FXFALSE,FXFALSE);
    } else {
      FX_grTexCombine_NoLock(GR_TMU1,
			     GR_COMBINE_FUNCTION_LOCAL,
			     GR_COMBINE_FACTOR_NONE,
			     GR_COMBINE_FUNCTION_LOCAL,
			     GR_COMBINE_FACTOR_NONE,
			     FXFALSE,FXFALSE);
		  
      FX_grTexCombine_NoLock(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);
    }
	  
    FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_LOCAL,
			     GR_COMBINE_FACTOR_NONE,
			     locala,
			     GR_COMBINE_OTHER_NONE,
			     FXFALSE);

    FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			     GR_COMBINE_FACTOR_ONE,
			     localc,
			     GR_COMBINE_OTHER_TEXTURE,
			     FXFALSE);
    break;
  case (FX_UM_E0_REPLACE | FX_UM_E1_MODULATE): /* Quake 2 and 3 */
    if (tmu1==FX_TMU1) {
      FX_grTexCombine_NoLock(GR_TMU1,
			     GR_COMBINE_FUNCTION_LOCAL,
			     GR_COMBINE_FACTOR_NONE,
			     GR_COMBINE_FUNCTION_ZERO,
			     GR_COMBINE_FACTOR_NONE,
			     FXFALSE,FXTRUE);
		  
      FX_grTexCombine_NoLock(GR_TMU0,
			     GR_COMBINE_FUNCTION_BLEND_OTHER,
			     GR_COMBINE_FACTOR_LOCAL,
			     GR_COMBINE_FUNCTION_BLEND_OTHER,
			     GR_COMBINE_FACTOR_LOCAL,
			     FXFALSE,FXFALSE);

    } else {
      FX_grTexCombine_NoLock(GR_TMU1,
			     GR_COMBINE_FUNCTION_LOCAL,
			     GR_COMBINE_FACTOR_NONE,
			     GR_COMBINE_FUNCTION_LOCAL,
			     GR_COMBINE_FACTOR_NONE,
			     FXFALSE,FXFALSE);
		  
      FX_grTexCombine_NoLock(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)
      FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       locala,
			       GR_COMBINE_OTHER_NONE,
			       FXFALSE);
    else
      FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			       GR_COMBINE_FACTOR_ONE,
			       locala,
			       GR_COMBINE_OTHER_NONE,
			       FXFALSE);


    FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			     GR_COMBINE_FACTOR_ONE,
			     localc,
			     GR_COMBINE_OTHER_TEXTURE,
			     FXFALSE);
    break;
  

  case (FX_UM_E0_MODULATE | FX_UM_E1_ADD): /* Quake 3 Sky */
    {
      GLboolean isalpha[FX_NUM_TMU];

      if(ti0->baseLevelInternalFormat==GL_ALPHA)
	isalpha[tmu0]=GL_TRUE;
      else
	isalpha[tmu0]=GL_FALSE;

      if(ti1->baseLevelInternalFormat==GL_ALPHA)
	isalpha[tmu1]=GL_TRUE;
      else
	isalpha[tmu1]=GL_FALSE;
	
      if(isalpha[FX_TMU1])
	FX_grTexCombine_NoLock(GR_TMU1,
			       GR_COMBINE_FUNCTION_ZERO,
			       GR_COMBINE_FACTOR_NONE,
			       GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       FXTRUE,FXFALSE);
      else
	FX_grTexCombine_NoLock(GR_TMU1,
			       GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       GR_COMBINE_FUNCTION_LOCAL,
			       GR_COMBINE_FACTOR_NONE,
			       FXFALSE,FXFALSE);

      if(isalpha[FX_TMU0])
	FX_grTexCombine_NoLock(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
	FX_grTexCombine_NoLock(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);

      FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			       GR_COMBINE_FACTOR_LOCAL,
			       localc,
			       GR_COMBINE_OTHER_TEXTURE,
			       FXFALSE);

      FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_SCALE_OTHER,
			       GR_COMBINE_FACTOR_LOCAL,
			       locala,		     
			       GR_COMBINE_OTHER_TEXTURE,
			       FXFALSE);
      break;
    }
  default:
    fprintf(stderr, "Unexpected dual texture mode encountered\n");
    break;
  }

  if (MESA_VERBOSE&VERBOSE_DRIVER) {
     fprintf(stderr,"fxmesa: fxSetupTextureDoubleTMU(...) End\n");
  }
}

/************************* No Texture ***************************/

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

  if (MESA_VERBOSE&VERBOSE_DRIVER) {
     fprintf(stderr,"fxmesa: fxSetupTextureNone(...)\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;
  
  FX_grAlphaCombine_NoLock(GR_COMBINE_FUNCTION_LOCAL,
			   GR_COMBINE_FACTOR_NONE,
			   locala,
			   GR_COMBINE_OTHER_NONE,
			   FXFALSE);

  FX_grColorCombine_NoLock(GR_COMBINE_FUNCTION_LOCAL,
			   GR_COMBINE_FACTOR_NONE,
			   localc,
			   GR_COMBINE_OTHER_NONE,
			   FXFALSE);

  fxMesa->lastUnitsMode=FX_UM_NONE;
}

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

static void fxSetupTexture_NoLock(GLcontext *ctx)
{
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
  GLuint tex2Denabled;

  if (MESA_VERBOSE&VERBOSE_DRIVER) {
     fprintf(stderr,"fxmesa: fxSetupTexture(...)\n");
  }

  /* Disable multipass texturing.
   */
  ctx->Driver.MultipassFunc = 0;

  /* Texture Combine, Color Combine and Alpha Combine.
   */  
  tex2Denabled = (ctx->Texture.ReallyEnabled & TEXTURE0_2D);

  if (fxMesa->emulateTwoTMUs)
     tex2Denabled |= (ctx->Texture.ReallyEnabled & TEXTURE1_2D);
  
  switch(tex2Denabled) {
  case TEXTURE0_2D:
    fxSetupTextureSingleTMU_NoLock(ctx,0);    
    break;
  case TEXTURE1_2D:
    fxSetupTextureSingleTMU_NoLock(ctx,1);
    break;
  case (TEXTURE0_2D|TEXTURE1_2D):
     if (fxMesa->haveTwoTMUs)
	fxSetupTextureDoubleTMU_NoLock(ctx);
     else {
	if (MESA_VERBOSE&VERBOSE_DRIVER)
	   fprintf(stderr, "fxmesa: enabling fake multitexture\n");

	fxSetupTextureSingleTMU_NoLock(ctx,0);
	ctx->Driver.MultipassFunc = fxMultipassTexture;
     }
    break;
  default:
    fxSetupTextureNone_NoLock(ctx);
    break;
  }
}

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

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

/* XXX consider supporting GL_INGR_blend_func_separate */
void fxDDBlendFunc(GLcontext *ctx, GLenum sfactor, GLenum dfactor)
{
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
  tfxUnitsState *us=&fxMesa->unitsState;
  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.
  */

  switch(sfactor) {
  case GL_ZERO:
    asfact=sfact=GR_BLEND_ZERO;
    break;
  case GL_ONE:
    asfact=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;
    break;
  case GL_ONE_MINUS_DST_ALPHA:
    sfact=GR_BLEND_ONE_MINUS_DST_ALPHA;
    asfact=GR_BLEND_ONE;
    break;
  case GL_SRC_ALPHA_SATURATE:
    sfact=GR_BLEND_ALPHA_SATURATE;
    asfact=GR_BLEND_ONE;
    break;
  case GL_SRC_COLOR:
  case GL_ONE_MINUS_SRC_COLOR:
    /* USELESS */
    asfact=sfact=GR_BLEND_ONE;
    break;
  default:
    asfact=sfact=GR_BLEND_ONE;
    break;
  }

  if((sfact!=us->blendSrcFuncRGB) ||
     (asfact!=us->blendSrcFuncAlpha)) {
    us->blendSrcFuncRGB=sfact;
    us->blendSrcFuncAlpha=asfact;
    fxMesa->new_state |= FX_NEW_BLEND;
    ctx->Driver.RenderStart = fxSetupFXUnits;
  }

  switch(dfactor) {
  case GL_ZERO:
    adfact=dfact=GR_BLEND_ZERO;
    break;
  case GL_ONE:
    adfact=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;
    break;
  case GL_ONE_MINUS_DST_ALPHA:
    /* dfact=GR_BLEND_ONE_MINUS_DST_ALPHA; */
    /* We can't do DST_ALPHA */
    dfact=GR_BLEND_ZERO;
    adfact=GR_BLEND_ZERO;
    break;
  case GL_SRC_ALPHA_SATURATE:
  case GL_DST_COLOR:
  case GL_ONE_MINUS_DST_COLOR:
    /* USELESS */
    adfact=dfact=GR_BLEND_ZERO;
    break;
  default:
    adfact=dfact=GR_BLEND_ZERO;
    break;
  }

  if((dfact!=us->blendDstFuncRGB) ||
     (adfact!=us->blendDstFuncAlpha)) {
    us->blendDstFuncRGB=dfact;
    us->blendDstFuncAlpha=adfact;
    fxMesa->new_state |= FX_NEW_BLEND;
    ctx->Driver.RenderStart = fxSetupFXUnits;
  }
}

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

  if(us->blendEnabled)
     FX_grAlphaBlendFunction(us->blendSrcFuncRGB,us->blendDstFuncRGB,
			  us->blendSrcFuncAlpha,us->blendDstFuncAlpha);
  else
     FX_grAlphaBlendFunction(GR_BLEND_ONE,GR_BLEND_ZERO,GR_BLEND_ONE,GR_BLEND_ZERO);
}
  
/************************************************************************/
/************************** Alpha Test SetUp ****************************/
/************************************************************************/

void fxDDAlphaFunc(GLcontext *ctx, GLenum func, GLclampf ref)
{
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
  tfxUnitsState *us=&fxMesa->unitsState;
  GrCmpFnc_t newfunc;

  switch(func) {
  case GL_NEVER:
    newfunc=GR_CMP_NEVER;
    break;
  case GL_LESS:
    newfunc=GR_CMP_LESS;
    break;
  case GL_EQUAL:
    newfunc=GR_CMP_EQUAL;
    break;
  case GL_LEQUAL:
    newfunc=GR_CMP_LEQUAL;
    break;
  case GL_GREATER:
    newfunc=GR_CMP_GREATER;
    break;
  case GL_NOTEQUAL:
    newfunc=GR_CMP_NOTEQUAL;
    break;
  case GL_GEQUAL:
    newfunc=GR_CMP_GEQUAL;
    break;
  case GL_ALWAYS:
    newfunc=GR_CMP_ALWAYS;
    break;
  default:
    fprintf(stderr,"fx Driver: internal error in fxDDAlphaFunc()\n");
    fxCloseHardware();
    exit(-1);
    break;
  }

  if(newfunc!=us->alphaTestFunc) {
    us->alphaTestFunc=newfunc;
    fxMesa->new_state |= FX_NEW_ALPHA;
    ctx->Driver.RenderStart = fxSetupFXUnits;
  }

  if(ctx->Color.AlphaRef!=us->alphaTestRefValue) {
    us->alphaTestRefValue=ctx->Color.AlphaRef;
    fxMesa->new_state |= FX_NEW_ALPHA;
    ctx->Driver.RenderStart = fxSetupFXUnits;
  }
}

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

  if(us->alphaTestEnabled) {
     FX_grAlphaTestFunction(us->alphaTestFunc);
     FX_grAlphaTestReferenceValue(us->alphaTestRefValue);
  } else
     FX_grAlphaTestFunction(GR_CMP_ALWAYS);
}

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

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

  switch(func) {
  case GL_NEVER:
    dfunc=GR_CMP_NEVER;
    break;
  case GL_LESS:
    dfunc=GR_CMP_LESS;
    break;
  case GL_GEQUAL:
    dfunc=GR_CMP_GEQUAL;
    break;
  case GL_LEQUAL:
    dfunc=GR_CMP_LEQUAL;
    break;
  case GL_GREATER:
    dfunc=GR_CMP_GREATER;
    break;
  case GL_NOTEQUAL:
    dfunc=GR_CMP_NOTEQUAL;
    break;
  case GL_EQUAL:
    dfunc=GR_CMP_EQUAL;
    break;
  case GL_ALWAYS:
    dfunc=GR_CMP_ALWAYS;
    break;
  default:
    fprintf(stderr,"fx Driver: internal error in fxDDDepthFunc()\n");
    fxCloseHardware();
    exit(-1);
    break;
  }

  if(dfunc!=us->depthTestFunc) {
    us->depthTestFunc=dfunc;
    fxMesa->new_state |= FX_NEW_DEPTH;
    ctx->Driver.RenderStart = fxSetupFXUnits;
  }

}

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

  if(flag!=us->depthMask) {
    us->depthMask=flag;
    fxMesa->new_state |= FX_NEW_DEPTH;
    ctx->Driver.RenderStart = fxSetupFXUnits;
  }
}

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

  if(us->depthTestEnabled)
     FX_grDepthBufferFunction(us->depthTestFunc);
  else
     FX_grDepthBufferFunction(GR_CMP_ALWAYS);

  FX_grDepthMask(us->depthMask);
}

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

GLboolean fxDDColorMask(GLcontext *ctx, 
			GLboolean r, GLboolean g, 
			GLboolean b, GLboolean a )
{
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
  fxMesa->new_state |= FX_NEW_COLOR_MASK;
  ctx->Driver.RenderStart = fxSetupFXUnits;
  (void) r; (void) g; (void) b; (void) a;
  return 1;
}

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

  FX_grColorMask(ctx->Color.ColorMask[RCOMP] ||
	      ctx->Color.ColorMask[GCOMP] ||
	      ctx->Color.ColorMask[BCOMP],
	      ctx->Color.ColorMask[ACOMP] && fxMesa->haveAlphaBuffer);
}



/************************************************************************/
/**************************** 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]);
    FX_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;
    }

    FX_grFogTable(fxMesa->fogTable);
    FX_grFogMode(GR_FOG_WITH_TABLE);
  }
  else {
    FX_grFogMode(GR_FOG_DISABLE);
  }
}

void fxDDFogfv( GLcontext *ctx, GLenum pname, const GLfloat *params )
{
   FX_CONTEXT(ctx)->new_state |= FX_NEW_FOG;
   ctx->Driver.RenderStart = fxSetupFXUnits;  /* XXX why is this here? */
}

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

/* This routine is used in managing the lock state, and therefore can't lock */
void fxSetScissorValues(GLcontext *ctx)
{
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
  int xmin, xmax;
  int ymin, ymax, check;

  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;
  } else {
    xmin=0;
    ymin=0;
    xmax=fxMesa->width;
    ymax=fxMesa->height;
    check=0;
  }
  xmin+=fxMesa->x_offset;
  xmax+=fxMesa->x_offset;
  ymin+=fxMesa->y_delta;
  ymax+=fxMesa->y_delta;
  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;
  FX_grClipWindow_NoLock(xmin, ymin, xmax, ymax);
}

static void fxSetupScissor(GLcontext *ctx)
{
  fxMesaContext fxMesa=(fxMesaContext)ctx->DriverCtx;
  if (!fxMesa->needClip) {
    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;
   ctx->Driver.RenderStart = fxSetupFXUnits;
}

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


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

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


static void fxSetupCull(GLcontext *ctx)
{
   if(ctx->Polygon.CullFlag) {
      switch(ctx->Polygon.CullFaceMode) {
      case GL_BACK:
	 if(ctx->Polygon.FrontFace==GL_CCW)
	    FX_grCullMode(GR_CULL_NEGATIVE);
	 else
	    FX_grCullMode(GR_CULL_POSITIVE);
	 break;
      case GL_FRONT:
	 if(ctx->Polygon.FrontFace==GL_CCW)
	    FX_grCullMode(GR_CULL_POSITIVE);
	 else
	    FX_grCullMode(GR_CULL_NEGATIVE);
	 break;
      case GL_FRONT_AND_BACK:
	 FX_grCullMode(GR_CULL_DISABLE);
	 break;
      default:
	 break;
      }
   } else
      FX_grCullMode(GR_CULL_DISABLE);
}


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

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

  if (MESA_VERBOSE&VERBOSE_DRIVER) {
     fprintf(stderr,"fxmesa: fxDDEnable(...)\n");
  }

  switch(cap) {
  case GL_ALPHA_TEST:
    if(state!=us->alphaTestEnabled) {
      us->alphaTestEnabled=state;
      fxMesa->new_state |= FX_NEW_ALPHA;
      ctx->Driver.RenderStart = fxSetupFXUnits;
    }
    break;
  case GL_BLEND:
    if(state!=us->blendEnabled) {
      us->blendEnabled=state;
      fxMesa->new_state |= FX_NEW_BLEND;
      ctx->Driver.RenderStart = fxSetupFXUnits;
    }
    break;
  case GL_DEPTH_TEST:
    if(state!=us->depthTestEnabled) {
      us->depthTestEnabled=state;
      fxMesa->new_state |= FX_NEW_DEPTH;
      ctx->Driver.RenderStart = fxSetupFXUnits;
    }
    break;
  case GL_SCISSOR_TEST:
     fxMesa->new_state |= FX_NEW_SCISSOR;
     ctx->Driver.RenderStart = fxSetupFXUnits;
     break;
  case GL_FOG:
     fxMesa->new_state |= FX_NEW_FOG;
     ctx->Driver.RenderStart = fxSetupFXUnits;
     break;
  case GL_CULL_FACE:
     fxMesa->new_state |= FX_NEW_CULL;
     ctx->Driver.RenderStart = fxSetupFXUnits;
     break;
  case GL_LINE_SMOOTH:
  case GL_POINT_SMOOTH:
  case GL_POLYGON_SMOOTH:
  case GL_TEXTURE_2D:
      fxMesa->new_state |= FX_NEW_TEXTURING;
      ctx->Driver.RenderStart = fxSetupFXUnits;
      break;
  default:
    ;  /* XXX no-op??? */
  }    
}

#if 0
/*
  Multipass to do GL_BLEND texture functions
  Cf*(1-Ct) has already been written to the buffer during the first pass
  Cc*Ct gets written during the second pass (in this function)
  Everything gets reset in the third call (in this function)
*/
static GLboolean fxMultipassBlend(struct vertex_buffer *VB, GLuint pass)
{
  GLcontext *ctx = VB->ctx;
  fxMesaContext fxMesa = FX_CONTEXT(ctx);

  switch (pass) {
  case 1:
    /* Add Cc*Ct */
    fxMesa->restoreUnitsState=fxMesa->unitsState;
    if (ctx->Depth.Mask) {
      /* We don't want to check or change the depth buffers */
      switch (ctx->Depth.Func) {
      case GL_NEVER:
      case GL_ALWAYS:
	break;
      default:
	fxDDDepthFunc(ctx, GL_EQUAL);
	break;
      }
      fxDDDepthMask(ctx, FALSE);
    }
    /* Enable Cc*Ct mode */
    /* ??? Set the Constant Color ??? */
    fxDDEnable(ctx, GL_BLEND, GL_TRUE);
    fxDDBlendFunc(ctx, ???, ???);
    fxSetupTextureSingleTMU(ctx, ???);
    fxSetupBlend(ctx);
    fxSetupDepthTest(ctx);
    break;

  case 2:
    /* Reset everything back to normal */
    fxMesa->unitsState = fxMesa->restoreUnitsState;
    fxMesa->setupdone &= ???;
    fxSetupTextureSingleTMU(ctx, ???);
    fxSetupBlend(ctx);
    fxSetupDepthTest(ctx);
    break;
  }

  return pass==1;
}
#endif

/************************************************************************/
/******************** Fake Multitexture Support *************************/
/************************************************************************/

/* Its considered cheeky to try to fake ARB multitexture by doing
 * multipass rendering, because it is not possible to emulate the full
 * spec in this way.  The fact is that the voodoo 2 supports only a
 * subset of the possible multitexturing modes, and it is possible to
 * support almost the same subset using multipass blending on the
 * voodoo 1.  In all other cases for both voodoo 1 and 2, we fall back
 * to software rendering, satisfying the spec if not the user.  
 */
static GLboolean fxMultipassTexture( struct vertex_buffer *VB, GLuint pass )
{
   GLcontext *ctx = VB->ctx;
   fxVertex *v = FX_DRIVER_DATA(VB)->verts;
   fxVertex *last = FX_DRIVER_DATA(VB)->last_vert;
   fxMesaContext fxMesa = FX_CONTEXT(ctx);

   switch (pass) {
   case 1:
      if (MESA_VERBOSE&(VERBOSE_DRIVER|VERBOSE_PIPELINE|VERBOSE_TEXTURE))
	 fprintf(stderr, "fxmesa: Second texture pass\n");

      for ( ; v != last ; v++) {
	 v->f[S0COORD] = v->f[S1COORD];
	 v->f[T0COORD] = v->f[T1COORD];
      }

      fxMesa->restoreUnitsState = fxMesa->unitsState; 
      fxMesa->tmu_source[0] = 1;

      if (ctx->Depth.Mask) {
	 switch (ctx->Depth.Func) {
	 case GL_NEVER:
	 case GL_ALWAYS:
	    break;
	 default:
	    fxDDDepthFunc( ctx, GL_EQUAL );
	    break;
	 }

	 fxDDDepthMask( ctx, GL_FALSE ); 
      }
      
      if (ctx->Texture.Unit[1].EnvMode == GL_MODULATE) {
	 fxDDEnable( ctx, GL_BLEND, GL_TRUE );
	 fxDDBlendFunc( ctx, GL_DST_COLOR, GL_ZERO );
      }

      fxSetupTextureSingleTMU( ctx, 1 ); 
      fxSetupBlend( ctx );
      fxSetupDepthTest( ctx );
      break;

   case 2:
      /* Restore original state.  
       */
      fxMesa->tmu_source[0] = 0;
      fxMesa->unitsState = fxMesa->restoreUnitsState;
      fxMesa->setupdone &= ~SETUP_TMU0;
      fxSetupTextureSingleTMU( ctx, 0 ); 
      fxSetupBlend( ctx );
      fxSetupDepthTest( ctx );
      break;
   }

   return pass == 1;      
}


/************************************************************************/
/************************** 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;
   ctx->Driver.RenderStart = fxSetupFXUnits;
}



/************************************************************************/
/****************************** Units SetUp *****************************/
/************************************************************************/
static void gl_print_fx_state_flags( const char *msg, GLuint flags )
{
   fprintf(stderr, 
	   "%s: (0x%x) %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, " : "");
}

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

  if (MESA_VERBOSE&VERBOSE_DRIVER)
     gl_print_fx_state_flags("fxmesa: 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_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;
/*       ctx->Driver.RenderStart = 0; */
  }
}



#else


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

int gl_fx_dummy_function_setup(void)
{
  return 0;
}

#endif  /* FX */
