/* -*- mode: c; c-basic-offset: 3 -*-
 *
 * Copyright 2000 VA Linux Systems Inc., Fremont, California.
 *
 * 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 (including the next
 * paragraph) 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
 * VA LINUX SYSTEMS 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.
 */

/*
 * New fixes:
 *	Daniel Borca <dborca@users.sourceforge.net>, 19 Jul 2004
 *
 * Original rewrite:
 *	Gareth Hughes <gareth@valinux.com>, 29 Sep - 1 Oct 2000
 *
 * Authors:
 *	Gareth Hughes <gareth@valinux.com>
 *	Brian Paul <brianp@valinux.com>
 *      Keith Whitwell <keith@tungstengraphics.com> (port to 3.5)
 *
 */

#include "main/mtypes.h"
#include "main/colormac.h"

#include "swrast/swrast.h"
#include "vbo/vbo.h"
#include "tnl/tnl.h"
#include "tnl/t_pipeline.h"
#include "swrast_setup/swrast_setup.h"

#include "tdfx_context.h"
#include "tdfx_state.h"
#include "tdfx_tex.h"
#include "tdfx_texman.h"
#include "tdfx_texstate.h"
#include "tdfx_render.h"



/* =============================================================
 * Alpha blending
 */

static void tdfxUpdateAlphaMode( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   GrCmpFnc_t func;
   GrAlphaBlendFnc_t srcRGB, dstRGB, srcA, dstA;
   GrAlphaBlendOp_t eqRGB, eqA;
   GrAlpha_t ref = (GLint) (ctx->Color.AlphaRef * 255.0);
   
   GLboolean isNapalm = TDFX_IS_NAPALM(fxMesa);
   GLboolean have32bpp = (ctx->Visual.greenBits == 8);
   GLboolean haveAlpha = fxMesa->haveHwAlpha;

   if ( TDFX_DEBUG & DEBUG_VERBOSE_API ) {
      fprintf( stderr, "%s()\n", __FUNCTION__ );
   }

   if ( ctx->Color.AlphaEnabled ) {
      func = ctx->Color.AlphaFunc - GL_NEVER + GR_CMP_NEVER;
   } else {
      func = GR_CMP_ALWAYS;
   }

   if ( ctx->Color.BlendEnabled
        && (fxMesa->Fallback & TDFX_FALLBACK_BLEND) == 0 ) {
      switch ( ctx->Color.BlendSrcRGB ) {
      case GL_ZERO:
	 srcRGB = GR_BLEND_ZERO;
	 break;
      case GL_ONE:
	 srcRGB = GR_BLEND_ONE;
	 break;
      case GL_DST_COLOR:
	 srcRGB = GR_BLEND_DST_COLOR;
	 break;
      case GL_ONE_MINUS_DST_COLOR:
	 srcRGB = GR_BLEND_ONE_MINUS_DST_COLOR;
	 break;
      case GL_SRC_ALPHA:
	 srcRGB = GR_BLEND_SRC_ALPHA;
	 break;
      case GL_ONE_MINUS_SRC_ALPHA:
	 srcRGB = GR_BLEND_ONE_MINUS_SRC_ALPHA;
	 break;
      case GL_DST_ALPHA:
	 srcRGB = haveAlpha ? GR_BLEND_DST_ALPHA : GR_BLEND_ONE/*JJJ*/;
	 break;
      case GL_ONE_MINUS_DST_ALPHA:
	 srcRGB = haveAlpha ? GR_BLEND_ONE_MINUS_DST_ALPHA : GR_BLEND_ZERO/*JJJ*/;
	 break;
      case GL_SRC_ALPHA_SATURATE:
	 srcRGB = GR_BLEND_ALPHA_SATURATE;
	 break;
      case GL_SRC_COLOR:
         if (isNapalm) {
	    srcRGB = GR_BLEND_SAME_COLOR_EXT;
	    break;
         }
      case GL_ONE_MINUS_SRC_COLOR:
         if (isNapalm) {
	    srcRGB = GR_BLEND_ONE_MINUS_SAME_COLOR_EXT;
	    break;
         }
      default:
	 srcRGB = GR_BLEND_ONE;
      }

      switch ( ctx->Color.BlendSrcA ) {
      case GL_ZERO:
	 srcA = GR_BLEND_ZERO;
	 break;
      case GL_ONE:
	 srcA = GR_BLEND_ONE;
	 break;
      case GL_SRC_COLOR:
      case GL_SRC_ALPHA:
	 srcA = have32bpp ? GR_BLEND_SRC_ALPHA : GR_BLEND_ONE/*JJJ*/;
	 break;
      case GL_ONE_MINUS_SRC_COLOR:
      case GL_ONE_MINUS_SRC_ALPHA:
	 srcA = have32bpp ? GR_BLEND_ONE_MINUS_SRC_ALPHA : GR_BLEND_ONE/*JJJ*/;
	 break;
      case GL_DST_COLOR:
      case GL_DST_ALPHA:
	 srcA = (have32bpp && haveAlpha) ? GR_BLEND_DST_ALPHA : GR_BLEND_ONE/*JJJ*/;
	 break;
      case GL_ONE_MINUS_DST_COLOR:
      case GL_ONE_MINUS_DST_ALPHA:
	 srcA = (have32bpp && haveAlpha) ? GR_BLEND_ONE_MINUS_DST_ALPHA : GR_BLEND_ZERO/*JJJ*/;
	 break;
      case GL_SRC_ALPHA_SATURATE:
         srcA = GR_BLEND_ONE;
	 break;
      default:
	 srcA = GR_BLEND_ONE;
      }

      switch ( ctx->Color.BlendDstRGB ) {
      case GL_ZERO:
	 dstRGB = GR_BLEND_ZERO;
	 break;
      case GL_ONE:
	 dstRGB = GR_BLEND_ONE;
	 break;
      case GL_SRC_COLOR:
	 dstRGB = GR_BLEND_SRC_COLOR;
	 break;
      case GL_ONE_MINUS_SRC_COLOR:
	 dstRGB = GR_BLEND_ONE_MINUS_SRC_COLOR;
	 break;
      case GL_SRC_ALPHA:
	 dstRGB = GR_BLEND_SRC_ALPHA;
	 break;
      case GL_ONE_MINUS_SRC_ALPHA:
	 dstRGB = GR_BLEND_ONE_MINUS_SRC_ALPHA;
	 break;
      case GL_DST_ALPHA:
	 dstRGB = haveAlpha ? GR_BLEND_DST_ALPHA : GR_BLEND_ONE/*JJJ*/;
	 break;
      case GL_ONE_MINUS_DST_ALPHA:
	 dstRGB = haveAlpha ? GR_BLEND_ONE_MINUS_DST_ALPHA : GR_BLEND_ZERO/*JJJ*/;
	 break;
      case GL_DST_COLOR:
         if (isNapalm) {
	    dstRGB = GR_BLEND_SAME_COLOR_EXT;
	    break;
         }
      case GL_ONE_MINUS_DST_COLOR:
         if (isNapalm) {
	    dstRGB = GR_BLEND_ONE_MINUS_SAME_COLOR_EXT;
	    break;
         }
      default:
	 dstRGB = GR_BLEND_ZERO;
      }

      switch ( ctx->Color.BlendDstA ) {
      case GL_ZERO:
	 dstA = GR_BLEND_ZERO;
	 break;
      case GL_ONE:
	 dstA = GR_BLEND_ONE;
	 break;
      case GL_SRC_COLOR:
      case GL_SRC_ALPHA:
	 dstA = have32bpp ? GR_BLEND_SRC_ALPHA : GR_BLEND_ZERO/*JJJ*/;
	 break;
      case GL_ONE_MINUS_SRC_COLOR:
      case GL_ONE_MINUS_SRC_ALPHA:
	 dstA = have32bpp ? GR_BLEND_ONE_MINUS_SRC_ALPHA : GR_BLEND_ZERO/*JJJ*/;
	 break;
      case GL_DST_COLOR:
      case GL_DST_ALPHA:
	 dstA = have32bpp ? GR_BLEND_DST_ALPHA : GR_BLEND_ONE/*JJJ*/;
	 break;
      case GL_ONE_MINUS_DST_COLOR:
      case GL_ONE_MINUS_DST_ALPHA:
	 dstA = have32bpp ? GR_BLEND_ONE_MINUS_DST_ALPHA : GR_BLEND_ZERO/*JJJ*/;
	 break;
      default:
	 dstA = GR_BLEND_ZERO;
      }

      switch ( ctx->Color.BlendEquationRGB ) {
      case GL_FUNC_SUBTRACT:
	 eqRGB = GR_BLEND_OP_SUB;
	 break;
      case GL_FUNC_REVERSE_SUBTRACT:
	 eqRGB = GR_BLEND_OP_REVSUB;
	 break;
      case GL_FUNC_ADD:
      default:
	 eqRGB = GR_BLEND_OP_ADD;
	 break;
      }

      switch ( ctx->Color.BlendEquationA ) {
      case GL_FUNC_SUBTRACT:
	 eqA = GR_BLEND_OP_SUB;
	 break;
      case GL_FUNC_REVERSE_SUBTRACT:
	 eqA = GR_BLEND_OP_REVSUB;
	 break;
      case GL_FUNC_ADD:
      default:
	 eqA = GR_BLEND_OP_ADD;
	 break;
      }
   } else {
      /* blend disabled */
      srcRGB = GR_BLEND_ONE;
      dstRGB = GR_BLEND_ZERO;
      eqRGB = GR_BLEND_OP_ADD;
      srcA = GR_BLEND_ONE;
      dstA = GR_BLEND_ZERO;
      eqA = GR_BLEND_OP_ADD;
   }

   if ( fxMesa->Color.AlphaFunc != func ) {
      fxMesa->Color.AlphaFunc = func;
      fxMesa->dirty |= TDFX_UPLOAD_ALPHA_TEST;
   }
   if ( fxMesa->Color.AlphaRef != ref ) {
      fxMesa->Color.AlphaRef = ref;
      fxMesa->dirty |= TDFX_UPLOAD_ALPHA_REF;
   }

   if ( fxMesa->Color.BlendSrcRGB != srcRGB ||
	fxMesa->Color.BlendDstRGB != dstRGB ||
	fxMesa->Color.BlendEqRGB != eqRGB ||
	fxMesa->Color.BlendSrcA != srcA ||
	fxMesa->Color.BlendDstA != dstA ||
	fxMesa->Color.BlendEqA != eqA )
   {
      fxMesa->Color.BlendSrcRGB = srcRGB;
      fxMesa->Color.BlendDstRGB = dstRGB;
      fxMesa->Color.BlendEqRGB = eqRGB;
      fxMesa->Color.BlendSrcA = srcA;
      fxMesa->Color.BlendDstA = dstA;
      fxMesa->Color.BlendEqA = eqA;
      fxMesa->dirty |= TDFX_UPLOAD_BLEND_FUNC;
   }
}

static void tdfxDDAlphaFunc( struct gl_context *ctx, GLenum func, GLfloat ref )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_ALPHA;
}

static void tdfxDDBlendEquationSeparate( struct gl_context *ctx, 
					 GLenum modeRGB, GLenum modeA )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );

   assert( modeRGB == modeA );
   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_ALPHA;
}

static void tdfxDDBlendFuncSeparate( struct gl_context *ctx,
				     GLenum sfactorRGB, GLenum dfactorRGB,
				     GLenum sfactorA, GLenum dfactorA )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_ALPHA;

   /*
    * XXX - Voodoo5 seems to suffer from precision problems in some
    * blend modes.  To pass all the conformance tests we'd have to
    * fall back to software for many modes.  Revisit someday.
    */
}

/* =============================================================
 * Stipple
 */

void tdfxUpdateStipple( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );
   GrStippleMode_t mode = GR_STIPPLE_DISABLE;

   if ( TDFX_DEBUG & DEBUG_VERBOSE_API ) {
      fprintf( stderr, "%s()\n", __FUNCTION__ );
   }

   FLUSH_BATCH( fxMesa );

   if (ctx->Polygon.StippleFlag) {
      mode = GR_STIPPLE_PATTERN;
   }

   if ( fxMesa->Stipple.Mode != mode ) {
      fxMesa->Stipple.Mode = mode;
      fxMesa->dirty |= TDFX_UPLOAD_STIPPLE;
   }
}


/* =============================================================
 * Depth testing
 */

static void tdfxUpdateZMode( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );
   GrCmpFnc_t func;
   FxI32 bias;
   FxBool mask;

   if ( TDFX_DEBUG & DEBUG_VERBOSE_API ) 
      fprintf( stderr, "%s()\n", __FUNCTION__ );


   bias = (FxI32) (ctx->Polygon.OffsetUnits * TDFX_DEPTH_BIAS_SCALE);

   if ( ctx->Depth.Test ) {
      func = ctx->Depth.Func - GL_NEVER + GR_CMP_NEVER;
      mask = ctx->Depth.Mask;
   }
   else {
      /* depth testing disabled */
      func = GR_CMP_ALWAYS;  /* fragments always pass */
      mask = FXFALSE;        /* zbuffer is not touched */
   }

   fxMesa->Depth.Clear = (FxU32) (ctx->DrawBuffer->_DepthMaxF * ctx->Depth.Clear);

   if ( fxMesa->Depth.Bias != bias ) {
      fxMesa->Depth.Bias = bias;
      fxMesa->dirty |= TDFX_UPLOAD_DEPTH_BIAS;
   }
   if ( fxMesa->Depth.Func != func ) {
      fxMesa->Depth.Func = func;
      fxMesa->dirty |= TDFX_UPLOAD_DEPTH_FUNC | TDFX_UPLOAD_DEPTH_MASK;
   }
   if ( fxMesa->Depth.Mask != mask ) {
      fxMesa->Depth.Mask = mask;
      fxMesa->dirty |= TDFX_UPLOAD_DEPTH_MASK;
   }
}

static void tdfxDDDepthFunc( struct gl_context *ctx, GLenum func )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_DEPTH;
}

static void tdfxDDDepthMask( struct gl_context *ctx, GLboolean flag )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_DEPTH;
}

static void tdfxDDClearDepth( struct gl_context *ctx, GLclampd d )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_DEPTH;
}



/* =============================================================
 * Stencil
 */


/* Evaluate all stencil state and make the Glide calls.
 */
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 */
}


static void tdfxUpdateStencil( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   if ( TDFX_DEBUG & DEBUG_VERBOSE_API ) {
      fprintf( stderr, "%s()\n", __FUNCTION__ );
   }

   if (fxMesa->haveHwStencil) {
      if (ctx->Stencil._Enabled) {
         fxMesa->Stencil.Function = ctx->Stencil.Function[0] - GL_NEVER + GR_CMP_NEVER;
         fxMesa->Stencil.RefValue = ctx->Stencil.Ref[0] & 0xff;
         fxMesa->Stencil.ValueMask = ctx->Stencil.ValueMask[0] & 0xff;
         fxMesa->Stencil.WriteMask = ctx->Stencil.WriteMask[0] & 0xff;
         fxMesa->Stencil.FailFunc = convertGLStencilOp(ctx->Stencil.FailFunc[0]);
         fxMesa->Stencil.ZFailFunc = convertGLStencilOp(ctx->Stencil.ZFailFunc[0]);
         fxMesa->Stencil.ZPassFunc = convertGLStencilOp(ctx->Stencil.ZPassFunc[0]);
         fxMesa->Stencil.Clear = ctx->Stencil.Clear & 0xff;
      }
      fxMesa->dirty |= TDFX_UPLOAD_STENCIL;
   }
}


static void
tdfxDDStencilFuncSeparate( struct gl_context *ctx, GLenum face, GLenum func,
                           GLint ref, GLuint mask )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_STENCIL;
}

static void
tdfxDDStencilMaskSeparate( struct gl_context *ctx, GLenum face, GLuint mask )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_STENCIL;
}

static void
tdfxDDStencilOpSeparate( struct gl_context *ctx, GLenum face, GLenum sfail,
                         GLenum zfail, GLenum zpass )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_STENCIL;
}


/* =============================================================
 * Fog - orthographic fog still not working
 */

static void tdfxUpdateFogAttrib( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   GrFogMode_t mode;
   GrColor_t color;

   if ( TDFX_DEBUG & DEBUG_VERBOSE_API ) {
      fprintf( stderr, "%s()\n", __FUNCTION__ );
   }

   if ( ctx->Fog.Enabled ) {
      if (ctx->Fog.FogCoordinateSource == GL_FOG_COORDINATE_EXT) {
         mode = GR_FOG_WITH_TABLE_ON_FOGCOORD_EXT;
      } else {
         mode = GR_FOG_WITH_TABLE_ON_Q;
      }
   } else {
      mode = GR_FOG_DISABLE;
   }

   color = TDFXPACKCOLOR888((GLubyte)(ctx->Fog.Color[0]*255.0F),
			    (GLubyte)(ctx->Fog.Color[1]*255.0F),
			    (GLubyte)(ctx->Fog.Color[2]*255.0F));

   if ( fxMesa->Fog.Mode != mode ) {
      fxMesa->Fog.Mode = mode;
      fxMesa->dirty |= TDFX_UPLOAD_FOG_MODE;
      fxMesa->dirty |= TDFX_UPLOAD_VERTEX_LAYOUT;/*JJJ*/
   }
   if ( fxMesa->Fog.Color != color ) {
      fxMesa->Fog.Color = color;
      fxMesa->dirty |= TDFX_UPLOAD_FOG_COLOR;
   }
   if ( fxMesa->Fog.TableMode != ctx->Fog.Mode ||
	fxMesa->Fog.Density != ctx->Fog.Density ||
	fxMesa->Fog.Near != ctx->Fog.Start ||
	fxMesa->Fog.Far != ctx->Fog.End )
   {
      switch( ctx->Fog.Mode ) {
      case GL_EXP:
	 fxMesa->Glide.guFogGenerateExp( fxMesa->Fog.Table, ctx->Fog.Density );
	 break;
      case GL_EXP2:
	 fxMesa->Glide.guFogGenerateExp2( fxMesa->Fog.Table, ctx->Fog.Density);
	 break;
      case GL_LINEAR:
	 fxMesa->Glide.guFogGenerateLinear( fxMesa->Fog.Table,
                                            ctx->Fog.Start, ctx->Fog.End );
	 break;
      }

      fxMesa->Fog.TableMode = ctx->Fog.Mode;
      fxMesa->Fog.Density = ctx->Fog.Density;
      fxMesa->Fog.Near = ctx->Fog.Start;
      fxMesa->Fog.Far = ctx->Fog.End;
      fxMesa->dirty |= TDFX_UPLOAD_FOG_TABLE;
   }
}

static void tdfxDDFogfv( struct gl_context *ctx, GLenum pname, const GLfloat *param )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_FOG;

   switch (pname) {
      case GL_FOG_COORDINATE_SOURCE_EXT: {
         GLenum p = (GLenum)*param;
         if (p == GL_FOG_COORDINATE_EXT) {
            _swrast_allow_vertex_fog(ctx, GL_TRUE);
            _swrast_allow_pixel_fog(ctx, GL_FALSE);
            _tnl_allow_vertex_fog( ctx, GL_TRUE);
            _tnl_allow_pixel_fog( ctx, GL_FALSE);
         } else {
            _swrast_allow_vertex_fog(ctx, GL_FALSE);
            _swrast_allow_pixel_fog(ctx, GL_TRUE);
            _tnl_allow_vertex_fog( ctx, GL_FALSE);
            _tnl_allow_pixel_fog( ctx, GL_TRUE);
         }
         break;
      }
      default:
         ;
   }
}


/* =============================================================
 * Clipping
 */

static int intersect_rect( drm_clip_rect_t *out,
			   const drm_clip_rect_t *a,
			   const drm_clip_rect_t *b)
{
   *out = *a;
   if (b->x1 > out->x1) out->x1 = b->x1;
   if (b->y1 > out->y1) out->y1 = b->y1;
   if (b->x2 < out->x2) out->x2 = b->x2;
   if (b->y2 < out->y2) out->y2 = b->y2;
   if (out->x1 >= out->x2) return 0;
   if (out->y1 >= out->y2) return 0;
   return 1;
}


/*
 * Examine XF86 cliprect list and scissor state to recompute our
 * cliprect list.
 */
void tdfxUpdateClipping( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   __DRIdrawable *dPriv = fxMesa->driDrawable;

   if ( TDFX_DEBUG & DEBUG_VERBOSE_API ) {
      fprintf( stderr, "%s()\n", __FUNCTION__ );
   }

   assert(ctx);
   assert(fxMesa);
   assert(dPriv);

   if ( dPriv->x != fxMesa->x_offset || dPriv->y != fxMesa->y_offset ||
	dPriv->w != fxMesa->width || dPriv->h != fxMesa->height ) {
      fxMesa->x_offset = dPriv->x;
      fxMesa->y_offset = dPriv->y;
      fxMesa->width = dPriv->w;
      fxMesa->height = dPriv->h;
      fxMesa->y_delta =
	 fxMesa->screen_height - fxMesa->y_offset - fxMesa->height;
      tdfxUpdateViewport( ctx );
   }

   if (fxMesa->scissoredClipRects && fxMesa->pClipRects) {
      free(fxMesa->pClipRects);
   }

   if (ctx->Scissor.Enabled) {
      /* intersect OpenGL scissor box with all cliprects to make a new
       * list of cliprects.
       */
      drm_clip_rect_t scissor;
      int x1 = ctx->Scissor.X + fxMesa->x_offset;
      int y1 = fxMesa->screen_height - fxMesa->y_delta
             - ctx->Scissor.Y - ctx->Scissor.Height;
      int x2 = x1 + ctx->Scissor.Width;
      int y2 = y1 + ctx->Scissor.Height;
      scissor.x1 = MAX2(x1, 0);
      scissor.y1 = MAX2(y1, 0);
      scissor.x2 = MAX2(x2, 0);
      scissor.y2 = MAX2(y2, 0);

      assert(scissor.x2 >= scissor.x1);
      assert(scissor.y2 >= scissor.y1);

      fxMesa->pClipRects = malloc(dPriv->numClipRects
                                  * sizeof(drm_clip_rect_t));
      if (fxMesa->pClipRects) {
         int i;
         fxMesa->numClipRects = 0;
         for (i = 0; i < dPriv->numClipRects; i++) {
            if (intersect_rect(&fxMesa->pClipRects[fxMesa->numClipRects],
                               &scissor, &dPriv->pClipRects[i])) {
               fxMesa->numClipRects++;
            }
         }
         fxMesa->scissoredClipRects = GL_TRUE;
      }
      else {
         /* out of memory, forgo scissor */
         fxMesa->numClipRects = dPriv->numClipRects;
         fxMesa->pClipRects = dPriv->pClipRects;
         fxMesa->scissoredClipRects = GL_FALSE;
      }
   }
   else {
      fxMesa->numClipRects = dPriv->numClipRects;
      fxMesa->pClipRects = dPriv->pClipRects;
      fxMesa->scissoredClipRects = GL_FALSE;
   }

   fxMesa->dirty |= TDFX_UPLOAD_CLIP;
}



/* =============================================================
 * Culling
 */

void tdfxUpdateCull( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   GrCullMode_t mode = GR_CULL_DISABLE;

   /* KW: don't need to check raster_primitive here as we don't
    * attempt to draw lines or points with triangles.
    */
   if ( ctx->Polygon.CullFlag ) {
      switch ( ctx->Polygon.CullFaceMode ) {
      case GL_FRONT:
	 if ( ctx->Polygon.FrontFace == GL_CCW ) {
	    mode = GR_CULL_POSITIVE;
	 } else {
	    mode = GR_CULL_NEGATIVE;
	 }
	 break;

      case GL_BACK:
	 if ( ctx->Polygon.FrontFace == GL_CCW ) {
	    mode = GR_CULL_NEGATIVE;
	 } else {
	    mode = GR_CULL_POSITIVE;
	 }
	 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;
      fxMesa->dirty |= TDFX_UPLOAD_CULL;
   }
}

static void tdfxDDCullFace( struct gl_context *ctx, GLenum mode )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_CULL;
}

static void tdfxDDFrontFace( struct gl_context *ctx, GLenum mode )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );

   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_CULL;
}


/* =============================================================
 * Line drawing.
 */

static void tdfxUpdateLine( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );

   if ( TDFX_DEBUG & DEBUG_VERBOSE_API ) {
      fprintf( stderr, "%s()\n", __FUNCTION__ );
   }

   FLUSH_BATCH( fxMesa );
   fxMesa->dirty |= TDFX_UPLOAD_LINE;
}


static void tdfxDDLineWidth( struct gl_context *ctx, GLfloat width )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );
   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_LINE;
}


/* =============================================================
 * Color Attributes
 */

static void tdfxDDColorMask( struct gl_context *ctx,
			     GLboolean r, GLboolean g,
			     GLboolean b, GLboolean a )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   FLUSH_BATCH( fxMesa );

   if ( fxMesa->Color.ColorMask[RCOMP] != r ||
	fxMesa->Color.ColorMask[GCOMP] != g ||
	fxMesa->Color.ColorMask[BCOMP] != b ||
	fxMesa->Color.ColorMask[ACOMP] != a ) {
      fxMesa->Color.ColorMask[RCOMP] = r;
      fxMesa->Color.ColorMask[GCOMP] = g;
      fxMesa->Color.ColorMask[BCOMP] = b;
      fxMesa->Color.ColorMask[ACOMP] = a;
      fxMesa->dirty |= TDFX_UPLOAD_COLOR_MASK;

      if (ctx->Visual.redBits < 8) {
         /* Can't do RGB colormasking in 16bpp mode. */
         /* We can completely ignore the alpha mask. */
	 FALLBACK( fxMesa, TDFX_FALLBACK_COLORMASK, (r != g || g != b) );
      }
   }
}


static void tdfxDDClearColor( struct gl_context *ctx,
			      const GLfloat color[4] )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   GLubyte c[4];
   FLUSH_BATCH( fxMesa );
   CLAMPED_FLOAT_TO_UBYTE(c[0], color[0]);
   CLAMPED_FLOAT_TO_UBYTE(c[1], color[1]);
   CLAMPED_FLOAT_TO_UBYTE(c[2], color[2]);
   CLAMPED_FLOAT_TO_UBYTE(c[3], color[3]);
   fxMesa->Color.ClearColor = TDFXPACKCOLOR888( c[0], c[1], c[2] );
   fxMesa->Color.ClearAlpha = c[3];
}


/* =============================================================
 * Light Model
 */

static void tdfxDDLightModelfv( struct gl_context *ctx, GLenum pname,
				const GLfloat *param )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   if ( pname == GL_LIGHT_MODEL_COLOR_CONTROL ) {
      FALLBACK( fxMesa, TDFX_FALLBACK_SPECULAR,
		(ctx->Light.Enabled &&
		 ctx->Light.Model.ColorControl == GL_SEPARATE_SPECULAR_COLOR ));
   }
}

static void tdfxDDShadeModel( struct gl_context *ctx, GLenum mode )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   /* FIXME: Can we implement native flat shading? */
   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_TEXTURE;
}


/* =============================================================
 * Scissor
 */

static void
tdfxDDScissor(struct gl_context * ctx, GLint x, GLint y, GLsizei w, GLsizei h)
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_CLIP;
}

/* =============================================================
 * Render
 */

static void tdfxUpdateRenderAttrib( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   FLUSH_BATCH( fxMesa );
   fxMesa->dirty |= TDFX_UPLOAD_RENDER_BUFFER;
}

/* =============================================================
 * Viewport
 */

void tdfxUpdateViewport( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   const GLfloat *v = ctx->Viewport._WindowMap.m;
   GLfloat *m = fxMesa->hw_viewport;

   m[MAT_SX] = v[MAT_SX];
   m[MAT_TX] = v[MAT_TX] + fxMesa->x_offset + TRI_X_OFFSET;
   m[MAT_SY] = v[MAT_SY];
   m[MAT_TY] = v[MAT_TY] + fxMesa->y_delta + TRI_Y_OFFSET;
   m[MAT_SZ] = v[MAT_SZ];
   m[MAT_TZ] = v[MAT_TZ];

   fxMesa->SetupNewInputs |= VERT_BIT_POS;
}


static void tdfxDDViewport( struct gl_context *ctx, GLint x, GLint y,
			    GLsizei w, GLsizei h )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_VIEWPORT;
}


static void tdfxDDDepthRange( struct gl_context *ctx, GLclampd nearVal, GLclampd farVal )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   FLUSH_BATCH( fxMesa );
   fxMesa->new_state |= TDFX_NEW_VIEWPORT;
}


/* =============================================================
 * State enable/disable
 */

static void tdfxDDEnable( struct gl_context *ctx, GLenum cap, GLboolean state )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT( ctx );

   switch ( cap ) {
   case GL_ALPHA_TEST:
      FLUSH_BATCH( fxMesa );
      fxMesa->new_state |= TDFX_NEW_ALPHA;
      break;

   case GL_BLEND:
      FLUSH_BATCH( fxMesa );
      fxMesa->new_state |= TDFX_NEW_ALPHA;
      FALLBACK( fxMesa, TDFX_FALLBACK_LOGICOP,
		(ctx->Color.ColorLogicOpEnabled &&
		 ctx->Color.LogicOp != GL_COPY)/*JJJ - more blending*/);
      break;

   case GL_CULL_FACE:
      FLUSH_BATCH( fxMesa );
      fxMesa->new_state |= TDFX_NEW_CULL;
      break;

   case GL_DEPTH_TEST:
      FLUSH_BATCH( fxMesa );
      fxMesa->new_state |= TDFX_NEW_DEPTH;
      break;

   case GL_DITHER:
      FLUSH_BATCH( fxMesa );
      if ( state ) {
	 fxMesa->Color.Dither = GR_DITHER_2x2;
      } else {
	 fxMesa->Color.Dither = GR_DITHER_DISABLE;
      }
      fxMesa->dirty |= TDFX_UPLOAD_DITHER;
      break;

   case GL_FOG:
      FLUSH_BATCH( fxMesa );
      fxMesa->new_state |= TDFX_NEW_FOG;
      break;

   case GL_COLOR_LOGIC_OP:
      FALLBACK( fxMesa, TDFX_FALLBACK_LOGICOP,
		(ctx->Color.ColorLogicOpEnabled &&
		 ctx->Color.LogicOp != GL_COPY));
      break;

   case GL_LIGHTING:
      FALLBACK( fxMesa, TDFX_FALLBACK_SPECULAR,
		(ctx->Light.Enabled &&
		 ctx->Light.Model.ColorControl == GL_SEPARATE_SPECULAR_COLOR ));
      break;

   case GL_LINE_SMOOTH:
      FLUSH_BATCH( fxMesa );
      fxMesa->new_state |= TDFX_NEW_LINE;
      break;

   case GL_LINE_STIPPLE:
      FALLBACK(fxMesa, TDFX_FALLBACK_LINE_STIPPLE, state);
      break;

   case GL_POLYGON_STIPPLE:
      FLUSH_BATCH(fxMesa);
      fxMesa->new_state |= TDFX_NEW_STIPPLE;
      break;

   case GL_SCISSOR_TEST:
      FLUSH_BATCH( fxMesa );
      fxMesa->new_state |= TDFX_NEW_CLIP;
      break;

   case GL_STENCIL_TEST:
      FLUSH_BATCH( fxMesa );
      FALLBACK( fxMesa, TDFX_FALLBACK_STENCIL, state && !fxMesa->haveHwStencil);
      fxMesa->new_state |= TDFX_NEW_STENCIL;
      break;

   case GL_TEXTURE_3D:
      FLUSH_BATCH( fxMesa );
      FALLBACK( fxMesa, TDFX_FALLBACK_TEXTURE_MAP, state); /* wrong */
      fxMesa->new_state |= TDFX_NEW_TEXTURE;
      break;

   case GL_TEXTURE_1D:
   case GL_TEXTURE_2D:
      FLUSH_BATCH( fxMesa );
      fxMesa->new_state |= TDFX_NEW_TEXTURE;
      break;

   default:
      return;
   }
}



/* Set the buffer used for drawing */
/* XXX support for separate read/draw buffers hasn't been tested */
static void tdfxDDDrawBuffer( struct gl_context *ctx, GLenum mode )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   if ( TDFX_DEBUG & DEBUG_VERBOSE_API ) {
      fprintf( stderr, "%s()\n", __FUNCTION__ );
   }

   FLUSH_BATCH( fxMesa );

   if (ctx->DrawBuffer->_NumColorDrawBuffers > 1) {
      FALLBACK( fxMesa, TDFX_FALLBACK_DRAW_BUFFER, GL_TRUE );
      return;
   }

   switch ( ctx->DrawBuffer->_ColorDrawBufferIndexes[0] ) {
   case BUFFER_FRONT_LEFT:
      fxMesa->DrawBuffer = fxMesa->ReadBuffer = GR_BUFFER_FRONTBUFFER;
      fxMesa->new_state |= TDFX_NEW_RENDER;
      FALLBACK( fxMesa, TDFX_FALLBACK_DRAW_BUFFER, GL_FALSE );
      break;
   case BUFFER_BACK_LEFT:
      fxMesa->DrawBuffer = fxMesa->ReadBuffer = GR_BUFFER_BACKBUFFER;
      fxMesa->new_state |= TDFX_NEW_RENDER;
      FALLBACK( fxMesa, TDFX_FALLBACK_DRAW_BUFFER, GL_FALSE );
      break;
   case -1:
      FX_grColorMaskv( ctx, false4 );
      FALLBACK( fxMesa, TDFX_FALLBACK_DRAW_BUFFER, GL_FALSE );
      break;
   default:
      FALLBACK( fxMesa, TDFX_FALLBACK_DRAW_BUFFER, GL_TRUE );
      break;
   }
}


static void tdfxDDReadBuffer( struct gl_context *ctx, GLenum mode )
{
   /* XXX ??? */
}


/* =============================================================
 * Polygon stipple
 */

static void tdfxDDPolygonStipple( struct gl_context *ctx, const GLubyte *mask )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   const GLubyte *m = mask;
   GLubyte q[4];
   int i,j,k;
   GLboolean allBitsSet;

/*     int active = (ctx->Polygon.StippleFlag &&  */
/*  		 fxMesa->reduced_prim == GL_TRIANGLES); */

   FLUSH_BATCH(fxMesa);
   fxMesa->Stipple.Pattern = 0xffffffff;
   fxMesa->dirty |= TDFX_UPLOAD_STIPPLE;
   fxMesa->new_state |= TDFX_NEW_STIPPLE;

   /* Check if the stipple pattern is fully opaque.  If so, use software
    * rendering.  This basically a trick to make sure the OpenGL conformance
    * test passes.
    */
   allBitsSet = GL_TRUE;
   for (i = 0; i < 32; i++) {
      if (((GLuint *) mask)[i] != 0xffffffff) {
         allBitsSet = GL_FALSE;
         break;
      }
   }
   if (allBitsSet) {
      fxMesa->haveHwStipple = GL_FALSE;
      return;
   }

   q[0] = mask[0];
   q[1] = mask[4];
   q[2] = mask[8];
   q[3] = mask[12];

   for (k = 0 ; k < 8 ; k++)
      for (j = 0 ; j < 4; j++)
	 for (i = 0 ; i < 4 ; i++,m++) {
	    if (*m != q[j]) {
	       fxMesa->haveHwStipple = GL_FALSE;
	       return;
	    }
         }

   fxMesa->haveHwStipple = GL_TRUE;
   fxMesa->Stipple.Pattern = ( (q[0] << 0) |
                               (q[1] << 8) |
                               (q[2] << 16) |
                               (q[3] << 24) );
}



static void tdfxDDRenderMode( struct gl_context *ctx, GLenum mode )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   FALLBACK( fxMesa, TDFX_FALLBACK_RENDER_MODE, (mode != GL_RENDER) );
}



static void tdfxDDPrintState( const char *msg, GLuint flags )
{
   fprintf( stderr,
	    "%s: (0x%x) %s%s%s%s%s%s%s%s%s%s%s%s%s\n",
	    msg,
	    flags,
	    (flags & TDFX_NEW_COLOR) ? "color, " : "",
	    (flags & TDFX_NEW_ALPHA) ? "alpha, " : "",
	    (flags & TDFX_NEW_DEPTH) ? "depth, " : "",
	    (flags & TDFX_NEW_RENDER) ? "render, " : "",
	    (flags & TDFX_NEW_FOG) ? "fog, " : "",
	    (flags & TDFX_NEW_STENCIL) ? "stencil, " : "",
	    (flags & TDFX_NEW_STIPPLE) ? "stipple, " : "",
	    (flags & TDFX_NEW_CLIP) ? "clip, " : "",
	    (flags & TDFX_NEW_VIEWPORT) ? "viewport, " : "",
	    (flags & TDFX_NEW_CULL) ? "cull, " : "",
	    (flags & TDFX_NEW_GLIDE) ? "glide, " : "",
	    (flags & TDFX_NEW_TEXTURE) ? "texture, " : "",
	    (flags & TDFX_NEW_CONTEXT) ? "context, " : "");
}



void tdfxDDUpdateHwState( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   int new_state = fxMesa->new_state;

   if ( TDFX_DEBUG & DEBUG_VERBOSE_API ) {
      fprintf( stderr, "%s()\n", __FUNCTION__ );
   }

   if ( new_state )
   {
      FLUSH_BATCH( fxMesa );

      fxMesa->new_state = 0;

      if ( 0 )
	 tdfxDDPrintState( "tdfxUpdateHwState", new_state );

      /* Update the various parts of the context's state.
       */
      if ( new_state & TDFX_NEW_ALPHA ) {
	 tdfxUpdateAlphaMode( ctx );
      }

      if ( new_state & TDFX_NEW_DEPTH )
	 tdfxUpdateZMode( ctx );

      if ( new_state & TDFX_NEW_FOG )
	 tdfxUpdateFogAttrib( ctx );

      if ( new_state & TDFX_NEW_CLIP )
	 tdfxUpdateClipping( ctx );

      if ( new_state & TDFX_NEW_STIPPLE )
	 tdfxUpdateStipple( ctx );

      if ( new_state & TDFX_NEW_CULL )
	 tdfxUpdateCull( ctx );

      if ( new_state & TDFX_NEW_LINE )
         tdfxUpdateLine( ctx );

      if ( new_state & TDFX_NEW_VIEWPORT )
	 tdfxUpdateViewport( ctx );

      if ( new_state & TDFX_NEW_RENDER )
	 tdfxUpdateRenderAttrib( ctx );

      if ( new_state & TDFX_NEW_STENCIL )
         tdfxUpdateStencil( ctx );

      if ( new_state & TDFX_NEW_TEXTURE ) {
	 tdfxUpdateTextureState( ctx );
      }
      else if ( new_state & TDFX_NEW_TEXTURE_BIND ) {
	 tdfxUpdateTextureBinding( ctx );
      }
   }

   if ( 0 ) {
      FxI32 bias = (FxI32) (ctx->Polygon.OffsetUnits * TDFX_DEPTH_BIAS_SCALE);

      if ( fxMesa->Depth.Bias != bias ) {
	 fxMesa->Depth.Bias = bias;
	 fxMesa->dirty |= TDFX_UPLOAD_DEPTH_BIAS;
      }
   }

   if ( fxMesa->dirty ) {
      LOCK_HARDWARE( fxMesa );
      tdfxEmitHwStateLocked( fxMesa );
      UNLOCK_HARDWARE( fxMesa );
   }
}


static void tdfxDDInvalidateState( struct gl_context *ctx, GLuint new_state )
{
   _swrast_InvalidateState( ctx, new_state );
   _swsetup_InvalidateState( ctx, new_state );
   _vbo_InvalidateState( ctx, new_state );
   _tnl_InvalidateState( ctx, new_state );
   TDFX_CONTEXT(ctx)->new_gl_state |= new_state;
}



/* Initialize the context's Glide state mirror.  These values will be
 * used as Glide function call parameters when the time comes.
 */
void tdfxInitState( tdfxContextPtr fxMesa )
{
   struct gl_context *ctx = fxMesa->glCtx;
   GLint i;

   fxMesa->ColorCombine.Function	= GR_COMBINE_FUNCTION_LOCAL;
   fxMesa->ColorCombine.Factor		= GR_COMBINE_FACTOR_NONE;
   fxMesa->ColorCombine.Local		= GR_COMBINE_LOCAL_ITERATED;
   fxMesa->ColorCombine.Other		= GR_COMBINE_OTHER_NONE;
   fxMesa->ColorCombine.Invert		= FXFALSE;
   fxMesa->AlphaCombine.Function	= GR_COMBINE_FUNCTION_LOCAL;
   fxMesa->AlphaCombine.Factor		= GR_COMBINE_FACTOR_NONE;
   fxMesa->AlphaCombine.Local		= GR_COMBINE_LOCAL_ITERATED;
   fxMesa->AlphaCombine.Other		= GR_COMBINE_OTHER_NONE;
   fxMesa->AlphaCombine.Invert		= FXFALSE;

   fxMesa->ColorCombineExt.SourceA	= GR_CMBX_ITRGB;
   fxMesa->ColorCombineExt.ModeA	= GR_FUNC_MODE_X;
   fxMesa->ColorCombineExt.SourceB	= GR_CMBX_ZERO;
   fxMesa->ColorCombineExt.ModeB	= GR_FUNC_MODE_ZERO;
   fxMesa->ColorCombineExt.SourceC	= GR_CMBX_ZERO;
   fxMesa->ColorCombineExt.InvertC	= FXTRUE;
   fxMesa->ColorCombineExt.SourceD	= GR_CMBX_ZERO;
   fxMesa->ColorCombineExt.InvertD	= FXFALSE;
   fxMesa->ColorCombineExt.Shift	= 0;
   fxMesa->ColorCombineExt.Invert	= FXFALSE;
   fxMesa->AlphaCombineExt.SourceA	= GR_CMBX_ITALPHA;
   fxMesa->AlphaCombineExt.ModeA	= GR_FUNC_MODE_X;
   fxMesa->AlphaCombineExt.SourceB	= GR_CMBX_ZERO;
   fxMesa->AlphaCombineExt.ModeB	= GR_FUNC_MODE_ZERO;
   fxMesa->AlphaCombineExt.SourceC	= GR_CMBX_ZERO;
   fxMesa->AlphaCombineExt.InvertC	= FXTRUE;
   fxMesa->AlphaCombineExt.SourceD	= GR_CMBX_ZERO;
   fxMesa->AlphaCombineExt.InvertD	= FXFALSE;
   fxMesa->AlphaCombineExt.Shift	= 0;
   fxMesa->AlphaCombineExt.Invert	= FXFALSE;

   fxMesa->sScale0 = fxMesa->tScale0 = 1.0;
   fxMesa->sScale1 = fxMesa->tScale1 = 1.0;

   fxMesa->TexPalette.Type = 0;
   fxMesa->TexPalette.Data = NULL;

   for ( i = 0 ; i < TDFX_NUM_TMU ; i++ ) {
      fxMesa->TexSource[i].StartAddress	= 0;
      fxMesa->TexSource[i].EvenOdd	= GR_MIPMAPLEVELMASK_EVEN;
      fxMesa->TexSource[i].Info		= NULL;

      fxMesa->TexCombine[i].FunctionRGB		= 0;
      fxMesa->TexCombine[i].FactorRGB		= 0;
      fxMesa->TexCombine[i].FunctionAlpha	= 0;
      fxMesa->TexCombine[i].FactorAlpha		= 0;
      fxMesa->TexCombine[i].InvertRGB		= FXFALSE;
      fxMesa->TexCombine[i].InvertAlpha		= FXFALSE;

      fxMesa->TexCombineExt[i].Alpha.SourceA	= 0;
      /* XXX more state to init here */
      fxMesa->TexCombineExt[i].Color.SourceA	= 0;
      fxMesa->TexCombineExt[i].EnvColor        = 0x0;

      fxMesa->TexParams[i].sClamp 	= GR_TEXTURECLAMP_WRAP;
      fxMesa->TexParams[i].tClamp	= GR_TEXTURECLAMP_WRAP;
      fxMesa->TexParams[i].minFilt	= GR_TEXTUREFILTER_POINT_SAMPLED;
      fxMesa->TexParams[i].magFilt	= GR_TEXTUREFILTER_BILINEAR;
      fxMesa->TexParams[i].mmMode	= GR_MIPMAP_DISABLE;
      fxMesa->TexParams[i].LODblend	= FXFALSE;
      fxMesa->TexParams[i].LodBias	= 0.0;

      fxMesa->TexState.EnvMode[i]	= ~0;
      fxMesa->TexState.TexFormat[i]	= ~0;
      fxMesa->TexState.Enabled[i]	= 0;
   }

   if ( ctx->Visual.doubleBufferMode) {
      fxMesa->DrawBuffer		= GR_BUFFER_BACKBUFFER;
      fxMesa->ReadBuffer		= GR_BUFFER_BACKBUFFER;
   } else {
      fxMesa->DrawBuffer		= GR_BUFFER_FRONTBUFFER;
      fxMesa->ReadBuffer		= GR_BUFFER_FRONTBUFFER;
   }

   fxMesa->Color.ClearColor		= 0x00000000;
   fxMesa->Color.ClearAlpha		= 0x00;
   fxMesa->Color.ColorMask[RCOMP]	= FXTRUE;
   fxMesa->Color.ColorMask[BCOMP]	= FXTRUE;
   fxMesa->Color.ColorMask[GCOMP]	= FXTRUE;
   fxMesa->Color.ColorMask[ACOMP]	= FXTRUE;
   fxMesa->Color.MonoColor		= 0xffffffff;

   fxMesa->Color.AlphaFunc		= GR_CMP_ALWAYS;
   fxMesa->Color.AlphaRef		= 0x00;
   fxMesa->Color.BlendSrcRGB		= GR_BLEND_ONE;
   fxMesa->Color.BlendDstRGB		= GR_BLEND_ZERO;
   fxMesa->Color.BlendSrcA		= GR_BLEND_ONE;
   fxMesa->Color.BlendSrcA		= GR_BLEND_ZERO;

   fxMesa->Color.Dither			= GR_DITHER_2x2;

   if ( fxMesa->glCtx->Visual.depthBits > 0 ) {
      fxMesa->Depth.Mode		= GR_DEPTHBUFFER_ZBUFFER;
   } else {
      fxMesa->Depth.Mode		= GR_DEPTHBUFFER_DISABLE;
   }
   fxMesa->Depth.Bias			= 0;
   fxMesa->Depth.Func			= GR_CMP_LESS;
   fxMesa->Depth.Clear			= 0; /* computed later */
   fxMesa->Depth.Mask			= FXTRUE;


   fxMesa->Fog.Mode			= GR_FOG_DISABLE;
   fxMesa->Fog.Color			= 0x00000000;
   fxMesa->Fog.Table			= NULL;
   fxMesa->Fog.Density			= 1.0;
   fxMesa->Fog.Near			= 1.0;
   fxMesa->Fog.Far			= 1.0;

   fxMesa->Stencil.Function		= GR_CMP_ALWAYS;
   fxMesa->Stencil.RefValue		= 0;
   fxMesa->Stencil.ValueMask		= 0xff;
   fxMesa->Stencil.WriteMask		= 0xff;
   fxMesa->Stencil.FailFunc		= 0;
   fxMesa->Stencil.ZFailFunc		= 0;
   fxMesa->Stencil.ZPassFunc		= 0;
   fxMesa->Stencil.Clear		= 0;

   fxMesa->Stipple.Mode                 = GR_STIPPLE_DISABLE;
   fxMesa->Stipple.Pattern              = 0xffffffff;

   fxMesa->Scissor.minX			= 0;
   fxMesa->Scissor.minY			= 0;
   fxMesa->Scissor.maxX			= 0;
   fxMesa->Scissor.maxY			= 0;

   fxMesa->Viewport.Mode		= GR_WINDOW_COORDS;
   fxMesa->Viewport.X			= 0;
   fxMesa->Viewport.Y			= 0;
   fxMesa->Viewport.Width		= 0;
   fxMesa->Viewport.Height		= 0;
   fxMesa->Viewport.Near		= 0.0;
   fxMesa->Viewport.Far			= 0.0;

   fxMesa->CullMode			= GR_CULL_DISABLE;

   fxMesa->Glide.ColorFormat		= GR_COLORFORMAT_ABGR;
   fxMesa->Glide.Origin			= GR_ORIGIN_LOWER_LEFT;
   fxMesa->Glide.Initialized		= FXFALSE;
}



void tdfxDDInitStateFuncs( struct gl_context *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   ctx->Driver.UpdateState		= tdfxDDInvalidateState;

   ctx->Driver.ClearColor		= tdfxDDClearColor;
   ctx->Driver.DrawBuffer		= tdfxDDDrawBuffer;
   ctx->Driver.ReadBuffer		= tdfxDDReadBuffer;

   ctx->Driver.AlphaFunc		= tdfxDDAlphaFunc;
   ctx->Driver.BlendEquationSeparate	= tdfxDDBlendEquationSeparate;
   ctx->Driver.BlendFuncSeparate	= tdfxDDBlendFuncSeparate;
   ctx->Driver.ClearDepth		= tdfxDDClearDepth;
   ctx->Driver.ColorMask		= tdfxDDColorMask;
   ctx->Driver.CullFace			= tdfxDDCullFace;
   ctx->Driver.FrontFace		= tdfxDDFrontFace;
   ctx->Driver.DepthFunc		= tdfxDDDepthFunc;
   ctx->Driver.DepthMask		= tdfxDDDepthMask;
   ctx->Driver.DepthRange		= tdfxDDDepthRange;
   ctx->Driver.Enable			= tdfxDDEnable;
   ctx->Driver.Fogfv			= tdfxDDFogfv;
   ctx->Driver.LightModelfv		= tdfxDDLightModelfv;
   ctx->Driver.LineWidth		= tdfxDDLineWidth;
   ctx->Driver.PolygonStipple		= tdfxDDPolygonStipple;
   ctx->Driver.RenderMode               = tdfxDDRenderMode;
   ctx->Driver.Scissor			= tdfxDDScissor;
   ctx->Driver.ShadeModel		= tdfxDDShadeModel;

   if ( fxMesa->haveHwStencil ) {
      ctx->Driver.StencilFuncSeparate	= tdfxDDStencilFuncSeparate;
      ctx->Driver.StencilMaskSeparate	= tdfxDDStencilMaskSeparate;
      ctx->Driver.StencilOpSeparate	= tdfxDDStencilOpSeparate;
   }

   ctx->Driver.Viewport			= tdfxDDViewport;
}
