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

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


/* =============================================================
 * Texture
 */

/*
 * These macros are used below when handling COMBINE_EXT.
 */
#define TEXENV_OPERAND_INVERTED(operand)                            \
  (((operand) == GL_ONE_MINUS_SRC_ALPHA)                            \
   || ((operand) == GL_ONE_MINUS_SRC_COLOR))
#define TEXENV_OPERAND_ALPHA(operand)                               \
  (((operand) == GL_SRC_ALPHA) || ((operand) == GL_ONE_MINUS_SRC_ALPHA))
#define TEXENV_SETUP_ARG_A(param, source, operand, iteratedAlpha)   \
    switch (source) {                                               \
    case GL_TEXTURE:                                                \
        param = GR_CMBX_LOCAL_TEXTURE_ALPHA;                        \
        break;                                                      \
    case GL_CONSTANT_EXT:                                           \
        param = GR_CMBX_TMU_CALPHA;                                 \
        break;                                                      \
    case GL_PRIMARY_COLOR_EXT:                                      \
        param = GR_CMBX_ITALPHA;                                    \
        break;                                                      \
    case GL_PREVIOUS_EXT:                                           \
        param = iteratedAlpha;                                      \
        break;                                                      \
    default:                                                        \
       /*                                                           \
        * This is here just to keep from getting                    \
        * compiler warnings.                                        \
        */                                                          \
        param = GR_CMBX_ZERO;                                       \
        break;                                                      \
    }

#define TEXENV_SETUP_ARG_RGB(param, source, operand, iteratedColor, iteratedAlpha) \
    if (!TEXENV_OPERAND_ALPHA(operand)) {                           \
        switch (source) {                                           \
        case GL_TEXTURE:                                            \
            param = GR_CMBX_LOCAL_TEXTURE_RGB;                      \
            break;                                                  \
        case GL_CONSTANT_EXT:                                       \
            param = GR_CMBX_TMU_CCOLOR;                             \
            break;                                                  \
        case GL_PRIMARY_COLOR_EXT:                                  \
            param = GR_CMBX_ITRGB;                                  \
            break;                                                  \
        case GL_PREVIOUS_EXT:                                       \
            param = iteratedColor;                                  \
            break;                                                  \
        default:                                                    \
           /*                                                       \
            * This is here just to keep from getting                \
            * compiler warnings.                                    \
            */                                                      \
            param = GR_CMBX_ZERO;                                   \
            break;                                                  \
        }                                                           \
    } else {                                                        \
        switch (source) {                                           \
        case GL_TEXTURE:                                            \
            param = GR_CMBX_LOCAL_TEXTURE_ALPHA;                    \
            break;                                                  \
        case GL_CONSTANT_EXT:                                       \
            param = GR_CMBX_TMU_CALPHA;                             \
            break;                                                  \
        case GL_PRIMARY_COLOR_EXT:                                  \
            param = GR_CMBX_ITALPHA;                                \
            break;                                                  \
        case GL_PREVIOUS_EXT:                                       \
            param = iteratedAlpha;                                  \
            break;                                                  \
        default:                                                    \
           /*                                                       \
            * This is here just to keep from getting                \
            * compiler warnings.                                    \
            */                                                      \
            param = GR_CMBX_ZERO;                                   \
            break;                                                  \
        }                                                           \
    }

#define TEXENV_SETUP_MODE_RGB(param, operand)                       \
    switch (operand) {                                              \
    case GL_SRC_COLOR:                                              \
    case GL_SRC_ALPHA:                                              \
        param = GR_FUNC_MODE_X;                                     \
        break;                                                      \
    case GL_ONE_MINUS_SRC_ALPHA:                                    \
    case GL_ONE_MINUS_SRC_COLOR:                                    \
        param = GR_FUNC_MODE_ONE_MINUS_X;                           \
        break;                                                      \
    default:                                                        \
        param = GR_FUNC_MODE_ZERO;                                  \
        break;                                                      \
    }

#define TEXENV_SETUP_MODE_A(param, operand)                         \
    switch (operand) {                                              \
    case GL_SRC_ALPHA:                                              \
        param = GR_FUNC_MODE_X;                                     \
        break;                                                      \
    case GL_ONE_MINUS_SRC_ALPHA:                                    \
        param = GR_FUNC_MODE_ONE_MINUS_X;                           \
        break;                                                      \
    default:                                                        \
        param = GR_FUNC_MODE_ZERO;                                  \
        break;                                                      \
    }



/*
 * Setup a texture environment on Voodoo5.
 * Return GL_TRUE for success, GL_FALSE for failure.
 * If we fail, we'll have to use software rendering.
 */
static GLboolean
SetupTexEnvNapalm(GLcontext *ctx, GLboolean useIteratedRGBA,
                  const struct gl_texture_unit *texUnit, GLenum baseFormat,
                  struct tdfx_texcombine_ext *env)
{
    tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
    GrTCCUColor_t incomingRGB, incomingAlpha;
    const GLenum envMode = texUnit->EnvMode;

    if (useIteratedRGBA) {
        incomingRGB = GR_CMBX_ITRGB;
        incomingAlpha = GR_CMBX_ITALPHA;
    }
    else {
        incomingRGB = GR_CMBX_OTHER_TEXTURE_RGB;
        incomingAlpha = GR_CMBX_OTHER_TEXTURE_ALPHA;
    }

    /* invariant: */
    env->Color.Shift = 0;
    env->Color.Invert = FXFALSE;
    env->Alpha.Shift = 0;
    env->Alpha.Invert = FXFALSE;

    switch (envMode) {
    case GL_REPLACE:
        /* -- Setup RGB combiner */
        if (baseFormat == GL_ALPHA) {
            /* Rv = Rf */
            env->Color.SourceA = incomingRGB;
        }
        else {
            /* Rv = Rt */
            env->Color.SourceA = GR_CMBX_LOCAL_TEXTURE_RGB;
        }
        env->Color.ModeA = GR_FUNC_MODE_X;
        env->Color.SourceB = GR_CMBX_ZERO;
        env->Color.ModeB = GR_FUNC_MODE_ZERO;
        env->Color.SourceC = GR_CMBX_ZERO;
        env->Color.InvertC = FXTRUE;
        env->Color.SourceD = GR_CMBX_ZERO;
        env->Color.InvertD = FXFALSE;
        /* -- Setup Alpha combiner */
        if (baseFormat == GL_LUMINANCE || baseFormat == GL_RGB) {
            /* Av = Af */
           env->Alpha.SourceD = incomingAlpha;
        }
        else {
            /* Av = At */
           env->Alpha.SourceD = GR_CMBX_LOCAL_TEXTURE_ALPHA;
        }
        env->Alpha.SourceA = GR_CMBX_ITALPHA;
        env->Alpha.ModeA = GR_FUNC_MODE_ZERO;
        env->Alpha.SourceB = GR_CMBX_ITALPHA;
        env->Alpha.ModeB = GR_FUNC_MODE_ZERO;
        env->Alpha.SourceC = GR_CMBX_ZERO;
        env->Alpha.InvertC = FXFALSE;
        env->Alpha.InvertD = FXFALSE;
        break;

    case GL_MODULATE:
        /* -- Setup RGB combiner */
        if (baseFormat == GL_ALPHA) {
            /* Rv = Rf */
           env->Color.SourceC = GR_CMBX_ZERO;
           env->Color.InvertC = FXTRUE;
        }
        else {
            /* Result = Frag * Tex */
           env->Color.SourceC = GR_CMBX_LOCAL_TEXTURE_RGB;
           env->Color.InvertC = FXFALSE;
        }
        env->Color.SourceA = incomingRGB;
        env->Color.ModeA = GR_FUNC_MODE_X;
        env->Color.SourceB = GR_CMBX_ZERO;
        env->Color.ModeB = GR_FUNC_MODE_ZERO;
        env->Color.SourceD = GR_CMBX_ZERO;
        env->Color.InvertD = FXFALSE;
        /* -- Setup Alpha combiner */
        if (baseFormat == GL_LUMINANCE || baseFormat == GL_RGB) {
            /* Av = Af */
           env->Alpha.SourceA = incomingAlpha;
           env->Alpha.SourceC = GR_CMBX_ZERO;
           env->Alpha.InvertC = FXTRUE;
        }
        else {
            /* Av = Af * At */
           env->Alpha.SourceA = GR_CMBX_LOCAL_TEXTURE_ALPHA;
           env->Alpha.SourceC = incomingAlpha;
           env->Alpha.InvertC = FXFALSE;
        }
        env->Alpha.ModeA = GR_FUNC_MODE_X;
        env->Alpha.SourceB = GR_CMBX_ITALPHA;
        env->Alpha.ModeB = GR_FUNC_MODE_ZERO;
        env->Alpha.SourceD = GR_CMBX_ZERO;
        env->Alpha.InvertD = FXFALSE;
        break;

    case GL_DECAL:
        /* -- Setup RGB combiner */
        if (baseFormat == GL_RGB) {
            /* Rv = Rt */
           env->Color.SourceB = GR_CMBX_ZERO;
           env->Color.ModeB = GR_FUNC_MODE_X;
           env->Color.SourceC = GR_CMBX_ZERO;
           env->Color.InvertC = FXTRUE;
           env->Color.SourceD = GR_CMBX_ZERO;
           env->Color.InvertD = FXFALSE;
        }
        else {
            /* Rv = Rf * (1 - At) + Rt * At */
           env->Color.SourceB = incomingRGB;
           env->Color.ModeB = GR_FUNC_MODE_NEGATIVE_X;
           env->Color.SourceC = GR_CMBX_LOCAL_TEXTURE_ALPHA;
           env->Color.InvertC = FXFALSE;
           env->Color.SourceD = GR_CMBX_B;
           env->Color.InvertD = FXFALSE;
        }
        env->Color.SourceA = GR_CMBX_LOCAL_TEXTURE_RGB;
        env->Color.ModeA = GR_FUNC_MODE_X;
        /* -- Setup Alpha combiner */
        /* Av = Af */
        env->Alpha.SourceA = incomingAlpha;
        env->Alpha.ModeA = GR_FUNC_MODE_X;
        env->Alpha.SourceB = GR_CMBX_ITALPHA;
        env->Alpha.ModeB = GR_FUNC_MODE_ZERO;
        env->Alpha.SourceC = GR_CMBX_ZERO;
        env->Alpha.InvertC = FXTRUE;
        env->Alpha.SourceD = GR_CMBX_ZERO;
        env->Alpha.InvertD = FXFALSE;
        break;

    case GL_BLEND:
        /* -- Setup RGB combiner */
        if (baseFormat == GL_ALPHA) {
            /* Rv = Rf */
            env->Color.SourceA = incomingRGB;
            env->Color.ModeA = GR_FUNC_MODE_X;
            env->Color.SourceB = GR_CMBX_ZERO;
            env->Color.ModeB = GR_FUNC_MODE_ZERO;
            env->Color.SourceC = GR_CMBX_ZERO;
            env->Color.InvertC = FXTRUE;
            env->Color.SourceD = GR_CMBX_ZERO;
            env->Color.InvertD = FXFALSE;
        }
        else {
            /* Rv = Rf * (1 - Rt) + Rc * Rt */
            env->Color.SourceA = GR_CMBX_TMU_CCOLOR;
            env->Color.ModeA = GR_FUNC_MODE_X;
            env->Color.SourceB = incomingRGB;
            env->Color.ModeB = GR_FUNC_MODE_NEGATIVE_X;
            env->Color.SourceC = GR_CMBX_LOCAL_TEXTURE_RGB;
            env->Color.InvertC = FXFALSE;
            env->Color.SourceD = GR_CMBX_B;
            env->Color.InvertD = FXFALSE;
        }
        /* -- Setup Alpha combiner */
        if (baseFormat == GL_LUMINANCE || baseFormat == GL_RGB) {
            /* Av = Af */
            env->Alpha.SourceA = incomingAlpha;
            env->Alpha.ModeA = GR_FUNC_MODE_X;
            env->Alpha.SourceB = GR_CMBX_ZERO;
            env->Alpha.ModeB = GR_FUNC_MODE_ZERO;
            env->Alpha.SourceC = GR_CMBX_ZERO;
            env->Alpha.InvertC = FXTRUE;
            env->Alpha.SourceD = GR_CMBX_ZERO;
            env->Alpha.InvertD = FXFALSE;
        }
        else if (baseFormat == GL_INTENSITY) {
            /* Av = Af * (1 - It) + Ac * It */
            env->Alpha.SourceA = GR_CMBX_TMU_CALPHA;
            env->Alpha.ModeA = GR_FUNC_MODE_X;
            env->Alpha.SourceB = incomingAlpha;
            env->Alpha.ModeB = GR_FUNC_MODE_NEGATIVE_X;
            env->Alpha.SourceC = GR_CMBX_LOCAL_TEXTURE_ALPHA;
            env->Alpha.InvertC = FXFALSE;
            env->Alpha.SourceD = GR_CMBX_B;
            env->Alpha.InvertD = FXFALSE;
        }
        else {
            /* Av = Af * At */
            env->Alpha.SourceA = GR_CMBX_LOCAL_TEXTURE_ALPHA;
            env->Alpha.ModeA = GR_FUNC_MODE_X;
            env->Alpha.SourceB = GR_CMBX_ITALPHA;
            env->Alpha.ModeB = GR_FUNC_MODE_ZERO;
            env->Alpha.SourceC = incomingAlpha;
            env->Alpha.InvertC = FXFALSE;
            env->Alpha.SourceD = GR_CMBX_ZERO;
            env->Alpha.InvertD = FXFALSE;
        }
        /* Also have to set up the tex env constant color */
        env->EnvColor = PACK_RGBA32(texUnit->EnvColor[0] * 255.0F,
                                    texUnit->EnvColor[1] * 255.0F,
                                    texUnit->EnvColor[2] * 255.0F,
                                    texUnit->EnvColor[3] * 255.0F);
        break;
    case GL_ADD:
        /* -- Setup RGB combiner */
        if (baseFormat == GL_ALPHA) {
            /* Rv = Rf */
           env->Color.SourceB = GR_CMBX_ZERO;
           env->Color.ModeB = GR_FUNC_MODE_ZERO;
        }
        else {
            /* Rv = Rf + Tt */
           env->Color.SourceB = GR_CMBX_LOCAL_TEXTURE_RGB;
           env->Color.ModeB = GR_FUNC_MODE_X;
        }
        env->Color.SourceA = incomingRGB;
        env->Color.ModeA = GR_FUNC_MODE_X;
        env->Color.SourceC = GR_CMBX_ZERO;
        env->Color.InvertC = FXTRUE;
        env->Color.SourceD = GR_CMBX_ZERO;
        env->Color.InvertD = FXFALSE;
        /* -- Setup Alpha combiner */
        if (baseFormat == GL_LUMINANCE || baseFormat == GL_RGB) {
            /* Av = Af */
           env->Alpha.SourceA = incomingAlpha;
           env->Alpha.SourceB = GR_CMBX_ITALPHA;
           env->Alpha.ModeB = GR_FUNC_MODE_ZERO;
           env->Alpha.SourceC = GR_CMBX_ZERO;
           env->Alpha.InvertC = FXTRUE;

        }
        else if (baseFormat == GL_INTENSITY) {
            /* Av = Af + It */
           env->Alpha.SourceA = incomingAlpha;
           env->Alpha.SourceB = GR_CMBX_LOCAL_TEXTURE_ALPHA;
           env->Alpha.ModeB = GR_FUNC_MODE_X;
           env->Alpha.SourceC = GR_CMBX_ZERO;
           env->Alpha.InvertC = FXTRUE;
        }
        else {
            /* Av = Af * At */
           env->Alpha.SourceA = GR_CMBX_LOCAL_TEXTURE_ALPHA;
           env->Alpha.SourceB = GR_CMBX_ITALPHA;
           env->Alpha.ModeB = GR_FUNC_MODE_ZERO;
           env->Alpha.SourceC = incomingAlpha;
           env->Alpha.InvertC = FXFALSE;
        }
        env->Alpha.ModeA = GR_FUNC_MODE_X;
        env->Alpha.SourceD = GR_CMBX_ZERO;
        env->Alpha.InvertD = FXFALSE;
        break;

    case GL_COMBINE_EXT:
        {
            FxU32 A_RGB, B_RGB, C_RGB, D_RGB;
            FxU32 Amode_RGB, Bmode_RGB;
            FxBool Cinv_RGB, Dinv_RGB, Ginv_RGB;
            FxU32 Shift_RGB;
            FxU32 A_A, B_A, C_A, D_A;
            FxU32 Amode_A, Bmode_A;
            FxBool Cinv_A, Dinv_A, Ginv_A;
            FxU32 Shift_A;

           /*
            *
            * In the formulas below, we write:
            *  o "1(x)" for the identity function applied to x,
            *    so 1(x) = x.
            *  o "0(x)" for the constant function 0, so
            *    0(x) = 0 for all values of x.
            *
            * Calculate the color combination.
            */
            Shift_RGB = texUnit->Combine.ScaleShiftRGB;
            Shift_A = texUnit->Combine.ScaleShiftA;
            switch (texUnit->Combine.ModeRGB) {
            case GL_REPLACE:
               /*
                * The formula is: Arg0
                * We implement this by the formula:
                *   (Arg0 + 0(0))*(1-0) + 0
                */
                TEXENV_SETUP_ARG_RGB(A_RGB,
                                     texUnit->Combine.SourceRGB[0],
                                     texUnit->Combine.OperandRGB[0],
                                     incomingRGB, incomingAlpha);
                TEXENV_SETUP_MODE_RGB(Amode_RGB,
                                      texUnit->Combine.OperandRGB[0]);
                B_RGB = C_RGB = D_RGB = GR_CMBX_ZERO;
                Bmode_RGB = GR_FUNC_MODE_ZERO;
                Cinv_RGB = FXTRUE;
                Dinv_RGB = Ginv_RGB = FXFALSE;
                break;
            case GL_MODULATE:
               /*
                * The formula is: Arg0 * Arg1
                *
                * We implement this by the formula
                *   (Arg0 + 0(0)) * Arg1 + 0(0)
                */
                TEXENV_SETUP_ARG_RGB(A_RGB,
                                     texUnit->Combine.SourceRGB[0],
                                     texUnit->Combine.OperandRGB[0],
                                     incomingRGB, incomingAlpha);
                TEXENV_SETUP_MODE_RGB(Amode_RGB,
                                      texUnit->Combine.OperandRGB[0]);
                B_RGB = GR_CMBX_ZERO;
                Bmode_RGB = GR_CMBX_ZERO;
                TEXENV_SETUP_ARG_RGB(C_RGB,
                                     texUnit->Combine.SourceRGB[1],
                                     texUnit->Combine.OperandRGB[1],
                                     incomingRGB, incomingAlpha);
                Cinv_RGB = TEXENV_OPERAND_INVERTED
                               (texUnit->Combine.OperandRGB[1]);
                D_RGB = GR_CMBX_ZERO;
                Dinv_RGB = Ginv_RGB = FXFALSE;
                break;
            case GL_ADD:
               /*
                * The formula is Arg0 + Arg1
                */
                TEXENV_SETUP_ARG_RGB(A_RGB,
                                     texUnit->Combine.SourceRGB[0],
                                     texUnit->Combine.OperandRGB[0],
                                     incomingRGB, incomingAlpha);
                TEXENV_SETUP_MODE_RGB(Amode_RGB,
                                      texUnit->Combine.OperandRGB[0]);
                TEXENV_SETUP_ARG_RGB(B_RGB,
                                     texUnit->Combine.SourceRGB[1],
                                     texUnit->Combine.OperandRGB[1],
                                     incomingRGB, incomingAlpha);
                TEXENV_SETUP_MODE_RGB(Bmode_RGB,
                                      texUnit->Combine.OperandRGB[1]);
                C_RGB = D_RGB = GR_CMBX_ZERO;
                Cinv_RGB = FXTRUE;
                Dinv_RGB = Ginv_RGB = FXFALSE;
                break;
            case GL_ADD_SIGNED_EXT:
               /*
                * The formula is: Arg0 + Arg1 - 0.5.
                * We compute this by calculating:
                *      (Arg0 - 1/2) + Arg1         if op0 is SRC_{COLOR,ALPHA}
                *      Arg0 + (Arg1 - 1/2)         if op1 is SRC_{COLOR,ALPHA}
                * If both op0 and op1 are ONE_MINUS_SRC_{COLOR,ALPHA}
                * we cannot implement the formula properly.
                */
                TEXENV_SETUP_ARG_RGB(A_RGB,
                                     texUnit->Combine.SourceRGB[0],
                                     texUnit->Combine.OperandRGB[0],
                                     incomingRGB, incomingAlpha);
                TEXENV_SETUP_ARG_RGB(B_RGB,
                                     texUnit->Combine.SourceRGB[1],
                                     texUnit->Combine.OperandRGB[1],
                                     incomingRGB, incomingAlpha);
                if (!TEXENV_OPERAND_INVERTED(texUnit->Combine.OperandRGB[0])) {
                   /*
                    * A is not inverted.  So, choose it.
                    */
                    Amode_RGB = GR_FUNC_MODE_X_MINUS_HALF;
                    if (!TEXENV_OPERAND_INVERTED
                            (texUnit->Combine.OperandRGB[1])) {
                        Bmode_RGB = GR_FUNC_MODE_X;
                    }
                    else {
                        Bmode_RGB = GR_FUNC_MODE_ONE_MINUS_X;
                    }
                }
                else {
                   /*
                    * A is inverted, so try to subtract 1/2
                    * from B.
                    */
                    Amode_RGB = GR_FUNC_MODE_ONE_MINUS_X;
                    if (!TEXENV_OPERAND_INVERTED
                            (texUnit->Combine.OperandRGB[1])) {
                        Bmode_RGB = GR_FUNC_MODE_X_MINUS_HALF;
                    }
                    else {
                       /*
                        * Both are inverted.  This is the case
                        * we cannot handle properly.  We just
                        * choose to not add the - 1/2.
                        */
                        Bmode_RGB = GR_FUNC_MODE_ONE_MINUS_X;
                        return GL_FALSE;
                    }
                }
                C_RGB = D_RGB = GR_CMBX_ZERO;
                Cinv_RGB = FXTRUE;
                Dinv_RGB = Ginv_RGB = FXFALSE;
                break;
            case GL_INTERPOLATE_EXT:
               /*
                * The formula is: Arg0 * Arg2 + Arg1 * (1 - Arg2).
                * We compute this by the formula:
                *            (Arg0 - Arg1) * Arg2 + Arg1
                *               == Arg0 * Arg2 - Arg1 * Arg2 + Arg1
                *               == Arg0 * Arg2 + Arg1 * (1 - Arg2)
                * However, if both Arg1 is ONE_MINUS_X, the HW does
                * not support it properly.
                */
                TEXENV_SETUP_ARG_RGB(A_RGB,
                                     texUnit->Combine.SourceRGB[0],
                                     texUnit->Combine.OperandRGB[0],
                                     incomingRGB, incomingAlpha);
                TEXENV_SETUP_MODE_RGB(Amode_RGB,
                                      texUnit->Combine.OperandRGB[0]);
                TEXENV_SETUP_ARG_RGB(B_RGB,
                                     texUnit->Combine.SourceRGB[1],
                                     texUnit->Combine.OperandRGB[1],
                                     incomingRGB, incomingAlpha);
                if (TEXENV_OPERAND_INVERTED(texUnit->Combine.OperandRGB[1])) {
                   /*
                    * This case is wrong.
                    */
                   Bmode_RGB = GR_FUNC_MODE_NEGATIVE_X;
                   return GL_FALSE;
                }
                else {
                    Bmode_RGB = GR_FUNC_MODE_NEGATIVE_X;
                }
               /*
                * The Source/Operand for the C value must
                * specify some kind of alpha value.
                */
                TEXENV_SETUP_ARG_A(C_RGB,
                                   texUnit->Combine.SourceRGB[2],
                                   texUnit->Combine.OperandRGB[2],
                                   incomingAlpha);
                Cinv_RGB = FXFALSE;
                D_RGB = GR_CMBX_B;
                Dinv_RGB = Ginv_RGB = FXFALSE;
                break;
            default:
               /*
                * This is here mostly to keep from getting
                * a compiler warning about these not being set.
                * However, this should set all the texture values
                * to zero.
                */
                A_RGB = B_RGB = C_RGB = D_RGB = GR_CMBX_ZERO;
                Amode_RGB = Bmode_RGB = GR_FUNC_MODE_X;
                Cinv_RGB = Dinv_RGB = Ginv_RGB = FXFALSE;
                break;
            }
           /*
            * Calculate the alpha combination.
            */
            switch (texUnit->Combine.ModeA) {
            case GL_REPLACE:
               /*
                * The formula is: Arg0
                * We implement this by the formula:
                *   (Arg0 + 0(0))*(1-0) + 0
                */
                TEXENV_SETUP_ARG_A(A_A,
                                   texUnit->Combine.SourceA[0],
                                   texUnit->Combine.OperandA[0],
                                   incomingAlpha);
                TEXENV_SETUP_MODE_A(Amode_A,
                                    texUnit->Combine.OperandA[0]);
                B_A = GR_CMBX_ITALPHA;
                Bmode_A = GR_FUNC_MODE_ZERO;
                C_A = D_A = GR_CMBX_ZERO;
                Cinv_A = FXTRUE;
                Dinv_A = Ginv_A = FXFALSE;
                break;
            case GL_MODULATE:
               /*
                * The formula is: Arg0 * Arg1
                *
                * We implement this by the formula
                *   (Arg0 + 0(0)) * Arg1 + 0(0)
                */
                TEXENV_SETUP_ARG_A(A_A,
                                   texUnit->Combine.SourceA[0],
                                   texUnit->Combine.OperandA[0],
                                   incomingAlpha);
                TEXENV_SETUP_MODE_A(Amode_A,
                                    texUnit->Combine.OperandA[0]);
                B_A = GR_CMBX_ZERO;
                Bmode_A = GR_CMBX_ZERO;
                TEXENV_SETUP_ARG_A(C_A,
                                   texUnit->Combine.SourceA[1],
                                   texUnit->Combine.OperandA[1],
                                   incomingAlpha);
                Cinv_A = TEXENV_OPERAND_INVERTED
                               (texUnit->Combine.OperandA[1]);
                D_A = GR_CMBX_ZERO;
                Dinv_A = Ginv_A = FXFALSE;
                break;
            case GL_ADD:
               /*
                * The formula is Arg0 + Arg1
                */
                TEXENV_SETUP_ARG_A(A_A,
                                   texUnit->Combine.SourceA[0],
                                   texUnit->Combine.OperandA[0],
                                   incomingAlpha);
                TEXENV_SETUP_MODE_A(Amode_A,
                                    texUnit->Combine.OperandA[0]);
                TEXENV_SETUP_ARG_A(B_A,
                                   texUnit->Combine.SourceA[1],
                                   texUnit->Combine.OperandA[1],
                                   incomingAlpha);
                TEXENV_SETUP_MODE_A(Bmode_A,
                                    texUnit->Combine.OperandA[1]);
                C_A = D_A = GR_CMBX_ZERO;
                Cinv_A = FXTRUE;
                Dinv_A = Ginv_A = FXFALSE;
                break;
            case GL_ADD_SIGNED_EXT:
               /*
                * The formula is: Arg0 + Arg1 - 0.5.
                * We compute this by calculating:
                *      (Arg0 - 1/2) + Arg1         if op0 is SRC_{COLOR,ALPHA}
                *      Arg0 + (Arg1 - 1/2)         if op1 is SRC_{COLOR,ALPHA}
                * If both op0 and op1 are ONE_MINUS_SRC_{COLOR,ALPHA}
                * we cannot implement the formula properly.
                */
                TEXENV_SETUP_ARG_A(A_A,
                                   texUnit->Combine.SourceA[0],
                                   texUnit->Combine.OperandA[0],
                                   incomingAlpha);
                TEXENV_SETUP_ARG_A(B_A,
                                   texUnit->Combine.SourceA[1],
                                   texUnit->Combine.OperandA[1],
                                   incomingAlpha);
                if (!TEXENV_OPERAND_INVERTED(texUnit->Combine.OperandA[0])) {
                   /*
                    * A is not inverted.  So, choose it.
                    */
                    Amode_A = GR_FUNC_MODE_X_MINUS_HALF;
                    if (!TEXENV_OPERAND_INVERTED
                            (texUnit->Combine.OperandA[1])) {
                        Bmode_A = GR_FUNC_MODE_X;
                    } else {
                        Bmode_A = GR_FUNC_MODE_ONE_MINUS_X;
                    }
                } else {
                   /*
                    * A is inverted, so try to subtract 1/2
                    * from B.
                    */
                    Amode_A = GR_FUNC_MODE_ONE_MINUS_X;
                    if (!TEXENV_OPERAND_INVERTED
                            (texUnit->Combine.OperandA[1])) {
                        Bmode_A = GR_FUNC_MODE_X_MINUS_HALF;
                    } else {
                       /*
                        * Both are inverted.  This is the case
                        * we cannot handle properly.  We just
                        * choose to not add the - 1/2.
                        */
                        Bmode_A = GR_FUNC_MODE_ONE_MINUS_X;
                        return GL_FALSE;
                    }
                }
                C_A = D_A = GR_CMBX_ZERO;
                Cinv_A = FXTRUE;
                Dinv_A = Ginv_A = FXFALSE;
                break;
            case GL_INTERPOLATE_EXT:
               /*
                * The formula is: Arg0 * Arg2 + Arg1 * (1 - Arg2).
                * We compute this by the formula:
                *            (Arg0 - Arg1) * Arg2 + Arg1
                *               == Arg0 * Arg2 - Arg1 * Arg2 + Arg1
                *               == Arg0 * Arg2 + Arg1 * (1 - Arg2)
                * However, if both Arg1 is ONE_MINUS_X, the HW does
                * not support it properly.
                */
                TEXENV_SETUP_ARG_A(A_A,
                                   texUnit->Combine.SourceA[0],
                                   texUnit->Combine.OperandA[0],
                                   incomingAlpha);
                TEXENV_SETUP_MODE_A(Amode_A,
                                    texUnit->Combine.OperandA[0]);
                TEXENV_SETUP_ARG_A(B_A,
                                   texUnit->Combine.SourceA[1],
                                   texUnit->Combine.OperandA[1],
                                   incomingAlpha);
                if (!TEXENV_OPERAND_INVERTED(texUnit->Combine.OperandA[1])) {
                    Bmode_A = GR_FUNC_MODE_NEGATIVE_X;
                }
                else {
                   /*
                    * This case is wrong.
                    */
                    Bmode_A = GR_FUNC_MODE_NEGATIVE_X;
                    return GL_FALSE;
                }
               /*
                * The Source/Operand for the C value must
                * specify some kind of alpha value.
                */
                TEXENV_SETUP_ARG_A(C_A,
                                   texUnit->Combine.SourceA[2],
                                   texUnit->Combine.OperandA[2],
                                   incomingAlpha);
                Cinv_A = FXFALSE;
                D_A = GR_CMBX_B;
                Dinv_A = Ginv_A = FXFALSE;
                break;
            default:
               /*
                * This is here mostly to keep from getting
                * a compiler warning about these not being set.
                * However, this should set all the alpha values
                * to one.
                */
                A_A = B_A = C_A = D_A = GR_CMBX_ZERO;
                Amode_A = Bmode_A = GR_FUNC_MODE_X;
                Cinv_A = Dinv_A = FXFALSE;
                Ginv_A = FXTRUE;
                break;
            }
           /*
            * Save the parameters.
            */
            env->Color.SourceA = A_RGB;
            env->Color.ModeA = Amode_RGB;
            env->Color.SourceB = B_RGB;
            env->Color.ModeB = Bmode_RGB;
            env->Color.SourceC = C_RGB;
            env->Color.InvertC = Cinv_RGB;
            env->Color.SourceD = D_RGB;
            env->Color.InvertD = Dinv_RGB;
            env->Color.Shift = Shift_RGB;
            env->Color.Invert = Ginv_RGB;
            env->Alpha.SourceA = A_A;
            env->Alpha.ModeA = Amode_A;
            env->Alpha.SourceB = B_A;
            env->Alpha.ModeB = Bmode_A;
            env->Alpha.SourceC = C_A;
            env->Alpha.InvertC = Cinv_A;
            env->Alpha.SourceD = D_A;
            env->Alpha.InvertD = Dinv_A;
            env->Alpha.Shift = Shift_A;
            env->Alpha.Invert = Ginv_A;
            env->EnvColor = PACK_RGBA32(texUnit->EnvColor[0] * 255.0F,
                                        texUnit->EnvColor[1] * 255.0F,
                                        texUnit->EnvColor[2] * 255.0F,
                                        texUnit->EnvColor[3] * 255.0F);
        }
        break;

    default:
        _mesa_problem(ctx, "%s: Bad envMode", __FUNCTION__);
    }

    fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_ENV;

    fxMesa->ColorCombineExt.SourceA = GR_CMBX_TEXTURE_RGB;
    fxMesa->ColorCombineExt.ModeA = GR_FUNC_MODE_X,
    fxMesa->ColorCombineExt.SourceB = GR_CMBX_ZERO;
    fxMesa->ColorCombineExt.ModeB = GR_FUNC_MODE_X;
    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->dirty |= TDFX_UPLOAD_COLOR_COMBINE;
    fxMesa->AlphaCombineExt.SourceA = GR_CMBX_TEXTURE_ALPHA;
    fxMesa->AlphaCombineExt.ModeA = GR_FUNC_MODE_X;
    fxMesa->AlphaCombineExt.SourceB = GR_CMBX_ZERO;
    fxMesa->AlphaCombineExt.ModeB = GR_FUNC_MODE_X;
    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->dirty |= TDFX_UPLOAD_ALPHA_COMBINE;
    return GL_TRUE; /* success */
}



/*
 * Setup the Voodoo3 texture environment for a single texture unit.
 * Return GL_TRUE for success, GL_FALSE for failure.
 * If failure, we'll use software rendering.
 */
static GLboolean
SetupSingleTexEnvVoodoo3(GLcontext *ctx, int unit,
                         GLenum envMode, GLenum baseFormat)
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   GrCombineLocal_t localc, locala;
   struct tdfx_combine alphaComb, colorComb;

   if (1 /*iteratedRGBA*/)
      localc = locala = GR_COMBINE_LOCAL_ITERATED;
   else
      localc = locala = GR_COMBINE_LOCAL_CONSTANT;

   switch (envMode) {
   case GL_DECAL:
      alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
      alphaComb.Factor = GR_COMBINE_FACTOR_NONE;
      alphaComb.Local = locala;
      alphaComb.Other = GR_COMBINE_OTHER_NONE;
      alphaComb.Invert = FXFALSE;
      colorComb.Function = GR_COMBINE_FUNCTION_BLEND;
      colorComb.Factor = GR_COMBINE_FACTOR_TEXTURE_ALPHA;
      colorComb.Local = localc;
      colorComb.Other = GR_COMBINE_OTHER_TEXTURE;
      colorComb.Invert = FXFALSE;
      break;
   case GL_MODULATE:
      alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
      alphaComb.Factor = GR_COMBINE_FACTOR_LOCAL;
      alphaComb.Local = locala;
      alphaComb.Other = GR_COMBINE_OTHER_TEXTURE;
      alphaComb.Invert = FXFALSE;
      if (baseFormat == GL_ALPHA) {
         colorComb.Function = GR_COMBINE_FUNCTION_LOCAL;
         colorComb.Factor = GR_COMBINE_FACTOR_NONE;
         colorComb.Local = localc;
         colorComb.Other = GR_COMBINE_OTHER_NONE;
         colorComb.Invert = FXFALSE;
      }
      else {
         colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
         colorComb.Factor = GR_COMBINE_FACTOR_LOCAL;
         colorComb.Local = localc;
         colorComb.Other = GR_COMBINE_OTHER_TEXTURE;
         colorComb.Invert = FXFALSE;
      }
      break;

   case GL_BLEND:
      /*
       * XXX we can't do real GL_BLEND mode.  These settings assume that
       * the TexEnv color is black and incoming fragment color is white.
       */
      if (baseFormat == GL_LUMINANCE || baseFormat == GL_RGB) {
         /* Av = Af */
         alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
         alphaComb.Factor = GR_COMBINE_FACTOR_NONE;
         alphaComb.Local = locala;
         alphaComb.Other = GR_COMBINE_OTHER_NONE;
         alphaComb.Invert = FXFALSE;
      }
      else if (baseFormat == GL_INTENSITY) {
         /* Av = Af * (1 - It) + Ac * It */
         alphaComb.Function = GR_COMBINE_FUNCTION_BLEND;
         alphaComb.Factor = GR_COMBINE_FACTOR_TEXTURE_ALPHA;
         alphaComb.Local = locala;
         alphaComb.Other = GR_COMBINE_OTHER_CONSTANT;
         alphaComb.Invert = FXFALSE;
      }
      else {
         /* Av = Af * At */
         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
         alphaComb.Factor = GR_COMBINE_FACTOR_LOCAL;
         alphaComb.Local = locala;
         alphaComb.Other = GR_COMBINE_OTHER_TEXTURE;
         alphaComb.Invert = FXFALSE;
      }
      if (baseFormat == GL_ALPHA) {
         colorComb.Function = GR_COMBINE_FUNCTION_LOCAL;
         colorComb.Factor = GR_COMBINE_FACTOR_NONE;
         colorComb.Local = localc;
         colorComb.Other = GR_COMBINE_OTHER_NONE;
         colorComb.Invert = FXFALSE;
      }
      else {
         colorComb.Function = GR_COMBINE_FUNCTION_BLEND;
         colorComb.Factor = GR_COMBINE_FACTOR_TEXTURE_RGB;
         colorComb.Local = localc;
         colorComb.Other = GR_COMBINE_OTHER_CONSTANT;
         colorComb.Invert = FXTRUE;
      }
      fxMesa->Color.MonoColor = PACK_RGBA32(
         ctx->Texture.Unit[unit].EnvColor[0] * 255.0f,
         ctx->Texture.Unit[unit].EnvColor[1] * 255.0f,
         ctx->Texture.Unit[unit].EnvColor[2] * 255.0f,
         ctx->Texture.Unit[unit].EnvColor[3] * 255.0f);
      fxMesa->dirty |= TDFX_UPLOAD_CONSTANT_COLOR;
      break;

   case GL_REPLACE:
      if ((baseFormat == GL_RGB) || (baseFormat == GL_LUMINANCE)) {
         alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
         alphaComb.Factor = GR_COMBINE_FACTOR_NONE;
         alphaComb.Local = locala;
         alphaComb.Other = GR_COMBINE_OTHER_NONE;
         alphaComb.Invert = FXFALSE;
      }
      else {
         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
         alphaComb.Factor = GR_COMBINE_FACTOR_ONE;
         alphaComb.Local = locala;
         alphaComb.Other = GR_COMBINE_OTHER_TEXTURE;
         alphaComb.Invert = FXFALSE;
      }
      if (baseFormat == GL_ALPHA) {
         colorComb.Function = GR_COMBINE_FUNCTION_LOCAL;
         colorComb.Factor = GR_COMBINE_FACTOR_NONE;
         colorComb.Local = localc;
         colorComb.Other = GR_COMBINE_OTHER_NONE;
         colorComb.Invert = FXFALSE;
      }
      else {
         colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
         colorComb.Factor = GR_COMBINE_FACTOR_ONE;
         colorComb.Local = localc;
         colorComb.Other = GR_COMBINE_OTHER_TEXTURE;
         colorComb.Invert = FXFALSE;
      }
      break;

   case GL_ADD:
      if (baseFormat == GL_ALPHA ||
          baseFormat == GL_LUMINANCE_ALPHA ||
          baseFormat == GL_RGBA) {
         /* product of texel and fragment alpha */
         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
         alphaComb.Factor = GR_COMBINE_FACTOR_LOCAL;
         alphaComb.Local = locala;
         alphaComb.Other = GR_COMBINE_OTHER_TEXTURE;
         alphaComb.Invert = FXFALSE;
      }
      else if (baseFormat == GL_LUMINANCE || baseFormat == GL_RGB) {
         /* fragment alpha is unchanged */
         alphaComb.Function = GR_COMBINE_FUNCTION_LOCAL;
         alphaComb.Factor = GR_COMBINE_FACTOR_NONE;
         alphaComb.Local = locala;
         alphaComb.Other = GR_COMBINE_OTHER_NONE;
         alphaComb.Invert = FXFALSE;
      }
      else {
         ASSERT(baseFormat == GL_INTENSITY);
         /* sum of texel and fragment alpha */
         alphaComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL,
         alphaComb.Factor = GR_COMBINE_FACTOR_ONE;
         alphaComb.Local = locala;
         alphaComb.Other = GR_COMBINE_OTHER_TEXTURE;
         alphaComb.Invert = FXFALSE;
      }
      if (baseFormat == GL_ALPHA) {
         /* rgb unchanged */
         colorComb.Function = GR_COMBINE_FUNCTION_LOCAL;
         colorComb.Factor = GR_COMBINE_FACTOR_NONE;
         colorComb.Local = localc;
         colorComb.Other = GR_COMBINE_OTHER_NONE;
         colorComb.Invert = FXFALSE;
      }
      else {
         /* sum of texel and fragment rgb */
         colorComb.Function = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL,
         colorComb.Factor = GR_COMBINE_FACTOR_ONE;
         colorComb.Local = localc;
         colorComb.Other = GR_COMBINE_OTHER_TEXTURE;
         colorComb.Invert = FXFALSE;
      }
      break;

   default: {
      (void) memcpy(&colorComb, &fxMesa->ColorCombine, sizeof(colorComb));
      (void) memcpy(&alphaComb, &fxMesa->AlphaCombine, sizeof(alphaComb));
      _mesa_problem(ctx, "bad texture env mode in %s", __FUNCTION__);
   }
   }

   if (colorComb.Function != fxMesa->ColorCombine.Function ||
       colorComb.Factor != fxMesa->ColorCombine.Factor ||
       colorComb.Local != fxMesa->ColorCombine.Local ||
       colorComb.Other != fxMesa->ColorCombine.Other ||
       colorComb.Invert != fxMesa->ColorCombine.Invert) {
      fxMesa->ColorCombine = colorComb;
      fxMesa->dirty |= TDFX_UPLOAD_COLOR_COMBINE;
   }

   if (alphaComb.Function != fxMesa->AlphaCombine.Function ||
       alphaComb.Factor != fxMesa->AlphaCombine.Factor ||
       alphaComb.Local != fxMesa->AlphaCombine.Local ||
       alphaComb.Other != fxMesa->AlphaCombine.Other ||
       alphaComb.Invert != fxMesa->AlphaCombine.Invert) {
      fxMesa->AlphaCombine = alphaComb;
      fxMesa->dirty |= TDFX_UPLOAD_ALPHA_COMBINE;
   }
   return GL_TRUE;
}


/*
 * Setup the Voodoo3 texture environment for dual texture units.
 * Return GL_TRUE for success, GL_FALSE for failure.
 * If failure, we'll use software rendering.
 */
static GLboolean
SetupDoubleTexEnvVoodoo3(GLcontext *ctx, int tmu0,
                         GLenum envMode0, GLenum baseFormat0,
                         GLenum envMode1, GLenum baseFormat1)
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   const GrCombineLocal_t locala = GR_COMBINE_LOCAL_ITERATED;
   const GrCombineLocal_t localc = GR_COMBINE_LOCAL_ITERATED;
   const int tmu1 = 1 - tmu0;

   if (envMode0 == GL_MODULATE && envMode1 == GL_MODULATE) {
      GLboolean isalpha[TDFX_NUM_TMU];

      isalpha[tmu0] = (baseFormat0 == GL_ALPHA);
      isalpha[tmu1] = (baseFormat1 == GL_ALPHA);

      if (isalpha[TDFX_TMU1]) {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_ZERO;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXTRUE;
         fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
      }
      else {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXFALSE;
         fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
      }
      if (isalpha[TDFX_TMU0]) {
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_LOCAL;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
      }
      else {
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_LOCAL;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_LOCAL;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
      }
      fxMesa->ColorCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
      fxMesa->ColorCombine.Factor = GR_COMBINE_FACTOR_LOCAL;
      fxMesa->ColorCombine.Local = localc;
      fxMesa->ColorCombine.Other = GR_COMBINE_OTHER_TEXTURE;
      fxMesa->ColorCombine.Invert = FXFALSE;
      fxMesa->AlphaCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
      fxMesa->AlphaCombine.Factor = GR_COMBINE_FACTOR_LOCAL;
      fxMesa->AlphaCombine.Local = locala;
      fxMesa->AlphaCombine.Other = GR_COMBINE_OTHER_TEXTURE;
      fxMesa->AlphaCombine.Invert = FXFALSE;
   }
   else if (envMode0 == GL_REPLACE && envMode1 == GL_BLEND) { /* Quake */
      if (tmu0 == TDFX_TMU1) {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXTRUE;
         fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_LOCAL;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_LOCAL;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
      }
      else {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXFALSE;
         fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_ONE_MINUS_LOCAL;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_ONE_MINUS_LOCAL;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
      }
      fxMesa->ColorCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
      fxMesa->ColorCombine.Factor = GR_COMBINE_FACTOR_ONE;
      fxMesa->ColorCombine.Local = localc;
      fxMesa->ColorCombine.Other = GR_COMBINE_OTHER_TEXTURE;
      fxMesa->ColorCombine.Invert = FXFALSE;
      fxMesa->AlphaCombine.Function = GR_COMBINE_FUNCTION_LOCAL;
      fxMesa->AlphaCombine.Factor = GR_COMBINE_FACTOR_NONE;
      fxMesa->AlphaCombine.Local = locala;
      fxMesa->AlphaCombine.Other = GR_COMBINE_OTHER_NONE;
      fxMesa->AlphaCombine.Invert = FXFALSE;
   }
   else if (envMode0 == GL_REPLACE && envMode1 == GL_MODULATE) {
      /* Quake 2/3 */
      if (tmu1 == TDFX_TMU1) {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_ZERO;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXFALSE;
         fxMesa->TexCombine[1].InvertAlpha = FXTRUE;
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_LOCAL;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_LOCAL;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
      }
      else {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXFALSE;
         fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_LOCAL;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_BLEND_OTHER;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
      }

      fxMesa->ColorCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
      fxMesa->ColorCombine.Factor = GR_COMBINE_FACTOR_ONE;
      fxMesa->ColorCombine.Local = localc;
      fxMesa->ColorCombine.Other = GR_COMBINE_OTHER_TEXTURE;
      fxMesa->ColorCombine.Invert = FXFALSE;
      if (baseFormat0 == GL_RGB) {
         fxMesa->AlphaCombine.Function = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->AlphaCombine.Factor = GR_COMBINE_FACTOR_NONE;
         fxMesa->AlphaCombine.Local = locala;
         fxMesa->AlphaCombine.Other = GR_COMBINE_OTHER_NONE;
         fxMesa->AlphaCombine.Invert = FXFALSE;
      }
      else {
         fxMesa->AlphaCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
         fxMesa->AlphaCombine.Factor = GR_COMBINE_FACTOR_ONE;
         fxMesa->AlphaCombine.Local = locala;
         fxMesa->AlphaCombine.Other = GR_COMBINE_OTHER_NONE;
         fxMesa->AlphaCombine.Invert = FXFALSE;
      }
   }
   else if (envMode0 == GL_MODULATE && envMode1 == GL_ADD) {
      /* Quake 3 sky */
      GLboolean isalpha[TDFX_NUM_TMU];

      isalpha[tmu0] = (baseFormat0 == GL_ALPHA);
      isalpha[tmu1] = (baseFormat1 == GL_ALPHA);

      if (isalpha[TDFX_TMU1]) {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_ZERO;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXTRUE;
         fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
      }
      else {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXFALSE;
         fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
      }
      if (isalpha[TDFX_TMU0]) {
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_SCALE_OTHER;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
      }
      else {
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
      }
      fxMesa->ColorCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
      fxMesa->ColorCombine.Factor = GR_COMBINE_FACTOR_LOCAL;
      fxMesa->ColorCombine.Local = localc;
      fxMesa->ColorCombine.Other = GR_COMBINE_OTHER_TEXTURE;
      fxMesa->ColorCombine.Invert = FXFALSE;
      fxMesa->AlphaCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
      fxMesa->AlphaCombine.Factor = GR_COMBINE_FACTOR_LOCAL;
      fxMesa->AlphaCombine.Local = locala;
      fxMesa->AlphaCombine.Other = GR_COMBINE_OTHER_TEXTURE;
      fxMesa->AlphaCombine.Invert = FXFALSE;
   }
   else if (envMode0 == GL_REPLACE && envMode1 == GL_ADD) {
      /* Vulpine sky */
      GLboolean isalpha[TDFX_NUM_TMU];

      isalpha[tmu0] = (baseFormat0 == GL_ALPHA);
      isalpha[tmu1] = (baseFormat1 == GL_ALPHA);

      if (isalpha[TDFX_TMU1]) {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_ZERO;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXTRUE;
         fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
      } else {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXFALSE;
         fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
      }

      if (isalpha[TDFX_TMU0]) {
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_SCALE_OTHER;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
      } else {
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
      }

      fxMesa->ColorCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
      fxMesa->ColorCombine.Factor = GR_COMBINE_FACTOR_ONE;
      fxMesa->ColorCombine.Local = localc;
      fxMesa->ColorCombine.Other = GR_COMBINE_OTHER_TEXTURE;
      fxMesa->ColorCombine.Invert = FXFALSE;
      fxMesa->AlphaCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
      fxMesa->AlphaCombine.Factor = GR_COMBINE_FACTOR_ONE;
      fxMesa->AlphaCombine.Local = locala;
      fxMesa->AlphaCombine.Other = GR_COMBINE_OTHER_TEXTURE;
      fxMesa->AlphaCombine.Invert = FXFALSE;
   }
   else if (envMode1 == GL_REPLACE) {
      /* Homeworld2 */

      fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_ZERO;
      fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
      fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_ZERO;
      fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
      fxMesa->TexCombine[1].InvertRGB = FXFALSE;
      fxMesa->TexCombine[1].InvertAlpha = FXFALSE;

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

      if ((baseFormat0 == GL_RGB) && (baseFormat0 == GL_LUMINANCE)) {
         fxMesa->AlphaCombine.Function = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->AlphaCombine.Factor = GR_COMBINE_FACTOR_NONE;
         fxMesa->AlphaCombine.Local = locala;
         fxMesa->AlphaCombine.Other = GR_COMBINE_OTHER_NONE;
         fxMesa->AlphaCombine.Invert = FXFALSE;
      } else {
         fxMesa->AlphaCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
         fxMesa->AlphaCombine.Factor = GR_COMBINE_FACTOR_ONE;
         fxMesa->AlphaCombine.Local = locala;
         fxMesa->AlphaCombine.Other = GR_COMBINE_OTHER_TEXTURE;
         fxMesa->AlphaCombine.Invert = FXFALSE;
      }
      if (baseFormat0 == GL_ALPHA) {
         fxMesa->ColorCombine.Function = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->ColorCombine.Factor = GR_COMBINE_FACTOR_NONE;
         fxMesa->ColorCombine.Local = localc;
         fxMesa->ColorCombine.Other = GR_COMBINE_OTHER_NONE;
         fxMesa->ColorCombine.Invert = FXFALSE;
      } else {
         fxMesa->ColorCombine.Function = GR_COMBINE_FUNCTION_SCALE_OTHER;
         fxMesa->ColorCombine.Factor = GR_COMBINE_FACTOR_ONE;
         fxMesa->ColorCombine.Local = localc;
         fxMesa->ColorCombine.Other = GR_COMBINE_OTHER_TEXTURE;
         fxMesa->ColorCombine.Invert = FXFALSE;
      }
   }
   else {
      _mesa_problem(ctx, "%s: Unexpected dual texture mode encountered", __FUNCTION__);
      return GL_FALSE;
   }

   fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_ENV;
   fxMesa->dirty |= TDFX_UPLOAD_COLOR_COMBINE;
   fxMesa->dirty |= TDFX_UPLOAD_ALPHA_COMBINE;
   return GL_TRUE;
}


/*
 * This function makes sure that the correct mipmap levels are loaded
 * in the right places in memory and then makes the Glide calls to
 * setup the texture source pointers.
 */
static void
setupSingleTMU(tdfxContextPtr fxMesa, struct gl_texture_object *tObj)
{
   struct tdfxSharedState *shared = (struct tdfxSharedState *) fxMesa->glCtx->Shared->DriverData;
   tdfxTexInfo *ti = TDFX_TEXTURE_DATA(tObj);
   const GLcontext *ctx = fxMesa->glCtx;

   /* Make sure we're not loaded incorrectly */
   if (ti->isInTM && !shared->umaTexMemory) {
      /* if doing filtering between mipmap levels, alternate mipmap levels
       * must be in alternate TMUs.
       */
      if (ti->LODblend) {
         if (ti->whichTMU != TDFX_TMU_SPLIT)
            tdfxTMMoveOutTM_NoLock(fxMesa, tObj);
      }
      else {
         if (ti->whichTMU == TDFX_TMU_SPLIT)
            tdfxTMMoveOutTM_NoLock(fxMesa, tObj);
      }
   }

   /* Make sure we're loaded correctly */
   if (!ti->isInTM) {
      /* Have to download the texture */
      if (shared->umaTexMemory) {
         tdfxTMMoveInTM_NoLock(fxMesa, tObj, TDFX_TMU0);
      }
      else {
         /* Voodoo3 (split texture memory) */
         if (ti->LODblend) {
            tdfxTMMoveInTM_NoLock(fxMesa, tObj, TDFX_TMU_SPLIT);
         }
         else {
#if 0
            /* XXX putting textures into the second memory bank when the
             * first bank is full is not working at this time.
             */
            if (fxMesa->haveTwoTMUs) {
               GLint memReq = fxMesa->Glide.grTexTextureMemRequired(
                                       GR_MIPMAPLEVELMASK_BOTH, &(ti->info));
               if (shared->freeTexMem[TDFX_TMU0] > memReq) {
                  tdfxTMMoveInTM_NoLock(fxMesa, tObj, TDFX_TMU0);
               }
               else {
                  tdfxTMMoveInTM_NoLock(fxMesa, tObj, TDFX_TMU1);
               }
            }
            else
#endif
            {
               tdfxTMMoveInTM_NoLock(fxMesa, tObj, TDFX_TMU0);
            }
         }
      }
   }

   if (ti->LODblend && ti->whichTMU == TDFX_TMU_SPLIT) {
      /* mipmap levels split between texture banks */
      GLint u;

      if (ti->info.format == GR_TEXFMT_P_8 && !ctx->Texture.SharedPalette) {
         fxMesa->TexPalette.Type = ti->paltype;
         fxMesa->TexPalette.Data = &(ti->palette);
         fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_PALETTE;
      }

      for (u = 0; u < 2; u++) {
         fxMesa->TexParams[u].sClamp = ti->sClamp;
         fxMesa->TexParams[u].tClamp = ti->tClamp;
         fxMesa->TexParams[u].minFilt = ti->minFilt;
         fxMesa->TexParams[u].magFilt = ti->magFilt;
         fxMesa->TexParams[u].mmMode = ti->mmMode;
         fxMesa->TexParams[u].LODblend = ti->LODblend;
         fxMesa->TexParams[u].LodBias = ctx->Texture.Unit[u].LodBias;
      }
      fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_PARAMS;

      fxMesa->TexSource[0].StartAddress = ti->tm[TDFX_TMU0]->startAddr;
      fxMesa->TexSource[0].EvenOdd = GR_MIPMAPLEVELMASK_ODD;
      fxMesa->TexSource[0].Info = &(ti->info);
      fxMesa->TexSource[1].StartAddress = ti->tm[TDFX_TMU1]->startAddr;
      fxMesa->TexSource[1].EvenOdd = GR_MIPMAPLEVELMASK_EVEN;
      fxMesa->TexSource[1].Info = &(ti->info);
      fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_SOURCE;
   }
   else {
      FxU32 tmu;

      if (ti->whichTMU == TDFX_TMU_BOTH)
         tmu = TDFX_TMU0;
      else
         tmu = ti->whichTMU;

      if (shared->umaTexMemory) {
         assert(ti->whichTMU == TDFX_TMU0);
         assert(tmu == TDFX_TMU0);
      }

      if (ti->info.format == GR_TEXFMT_P_8 && !ctx->Texture.SharedPalette) {
         fxMesa->TexPalette.Type = ti->paltype;
         fxMesa->TexPalette.Data = &(ti->palette);
         fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_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 (fxMesa->TexParams[tmu].sClamp != ti->sClamp ||
          fxMesa->TexParams[tmu].tClamp != ti->tClamp ||
          fxMesa->TexParams[tmu].minFilt != ti->minFilt ||
          fxMesa->TexParams[tmu].magFilt != ti->magFilt ||
          fxMesa->TexParams[tmu].mmMode != ti->mmMode ||
          fxMesa->TexParams[tmu].LODblend != FXFALSE ||
          fxMesa->TexParams[tmu].LodBias != ctx->Texture.Unit[tmu].LodBias) {
         fxMesa->TexParams[tmu].sClamp = ti->sClamp;
         fxMesa->TexParams[tmu].tClamp = ti->tClamp;
         fxMesa->TexParams[tmu].minFilt = ti->minFilt;
         fxMesa->TexParams[tmu].magFilt = ti->magFilt;
         fxMesa->TexParams[tmu].mmMode = ti->mmMode;
         fxMesa->TexParams[tmu].LODblend = FXFALSE;
         fxMesa->TexParams[tmu].LodBias = ctx->Texture.Unit[tmu].LodBias;
         fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_PARAMS;
      }

      /* Glide texture source info */
      fxMesa->TexSource[0].Info = NULL;
      fxMesa->TexSource[1].Info = NULL;
      if (ti->tm[tmu]) {
         fxMesa->TexSource[tmu].StartAddress = ti->tm[tmu]->startAddr;
         fxMesa->TexSource[tmu].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
         fxMesa->TexSource[tmu].Info = &(ti->info);
         fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_SOURCE;
      }
   }

   fxMesa->sScale0 = ti->sScale;
   fxMesa->tScale0 = ti->tScale;
}

static void
selectSingleTMUSrc(tdfxContextPtr fxMesa, GLint tmu, FxBool LODblend)
{
   if (LODblend) {
      fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_BLEND;
      fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_ONE_MINUS_LOD_FRACTION;
      fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_BLEND;
      fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_ONE_MINUS_LOD_FRACTION;
      fxMesa->TexCombine[0].InvertRGB = FXFALSE;
      fxMesa->TexCombine[0].InvertAlpha = FXFALSE;

      if (fxMesa->haveTwoTMUs) {
         const struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
         const struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData;
         int tmu;

         if (shared->umaTexMemory)
            tmu = GR_TMU0;
         else
            tmu = GR_TMU1;

         fxMesa->TexCombine[tmu].FunctionRGB = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[tmu].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[tmu].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[tmu].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[tmu].InvertRGB = FXFALSE;
         fxMesa->TexCombine[tmu].InvertAlpha = FXFALSE;
      }
      fxMesa->tmuSrc = TDFX_TMU_SPLIT;
   }
   else {
      if (tmu != TDFX_TMU1) {
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
         if (fxMesa->haveTwoTMUs) {
            fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_ZERO;
            fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
            fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_ZERO;
            fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
            fxMesa->TexCombine[1].InvertRGB = FXFALSE;
            fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
         }
         fxMesa->tmuSrc = TDFX_TMU0;
      }
      else {
         fxMesa->TexCombine[1].FunctionRGB = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorRGB = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].FunctionAlpha = GR_COMBINE_FUNCTION_LOCAL;
         fxMesa->TexCombine[1].FactorAlpha = GR_COMBINE_FACTOR_NONE;
         fxMesa->TexCombine[1].InvertRGB = FXFALSE;
         fxMesa->TexCombine[1].InvertAlpha = FXFALSE;
         /* GR_COMBINE_FUNCTION_SCALE_OTHER doesn't work ?!? */
         fxMesa->TexCombine[0].FunctionRGB = GR_COMBINE_FUNCTION_BLEND;
         fxMesa->TexCombine[0].FactorRGB = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].FunctionAlpha = GR_COMBINE_FUNCTION_BLEND;
         fxMesa->TexCombine[0].FactorAlpha = GR_COMBINE_FACTOR_ONE;
         fxMesa->TexCombine[0].InvertRGB = FXFALSE;
         fxMesa->TexCombine[0].InvertAlpha = FXFALSE;
         fxMesa->tmuSrc = TDFX_TMU1;
      }
   }

   fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_ENV;
}

#if 0
static void print_state(tdfxContextPtr fxMesa)
{
   GLcontext *ctx = fxMesa->glCtx;
   struct gl_texture_object *tObj0 = ctx->Texture.Unit[0]._Current;
   struct gl_texture_object *tObj1 = ctx->Texture.Unit[1]._Current;
   GLenum base0 = tObj0->Image[0][tObj0->BaseLevel] ? tObj0->Image[0][tObj0->BaseLevel]->Format : 99;
   GLenum base1 = tObj1->Image[0][tObj1->BaseLevel] ? tObj1->Image[0][tObj1->BaseLevel]->Format : 99;

   printf("Unit 0: Enabled:  GL=%d   Gr=%d\n", ctx->Texture.Unit[0]._ReallyEnabled,
          fxMesa->TexState.Enabled[0]);
   printf("   EnvMode: GL=0x%x  Gr=0x%x\n", ctx->Texture.Unit[0].EnvMode,
          fxMesa->TexState.EnvMode[0]);
   printf("   BaseFmt: GL=0x%x  Gr=0x%x\n", base0, fxMesa->TexState.TexFormat[0]);


   printf("Unit 1: Enabled:  GL=%d  Gr=%d\n", ctx->Texture.Unit[1]._ReallyEnabled,
          fxMesa->TexState.Enabled[1]);
   printf("   EnvMode: GL=0x%x  Gr:0x%x\n", ctx->Texture.Unit[1].EnvMode,
          fxMesa->TexState.EnvMode[1]);
   printf("   BaseFmt: GL=0x%x  Gr:0x%x\n", base1, fxMesa->TexState.TexFormat[1]);
}
#endif

/*
 * When we're only using a single texture unit, we always use the 0th
 * Glide/hardware unit, regardless if it's GL_TEXTURE0_ARB or GL_TEXTURE1_ARB
 * that's enalbed.
 * Input:  ctx - the context
 *         unit - the OpenGL texture unit to use.
 */
static void setupTextureSingleTMU(GLcontext * ctx, GLuint unit)
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   tdfxTexInfo *ti;
   struct gl_texture_object *tObj;
   int tmu;
   GLenum envMode, baseFormat;

   tObj = ctx->Texture.Unit[unit]._Current;
   if (tObj->Image[0][tObj->BaseLevel]->Border > 0) {
      FALLBACK(fxMesa, TDFX_FALLBACK_TEXTURE_BORDER, GL_TRUE);
      return;
   }

   setupSingleTMU(fxMesa, tObj);

   ti = TDFX_TEXTURE_DATA(tObj);
   if (ti->whichTMU == TDFX_TMU_BOTH)
      tmu = TDFX_TMU0;
   else
      tmu = ti->whichTMU;

   if (fxMesa->tmuSrc != tmu) {
      selectSingleTMUSrc(fxMesa, tmu, ti->LODblend);
   }

   if (ti->reloadImages)
      fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_IMAGES;

   /* Check if we really need to update the texenv state */
   envMode = ctx->Texture.Unit[unit].EnvMode;
   baseFormat = tObj->Image[0][tObj->BaseLevel]->_BaseFormat;

   if (TDFX_IS_NAPALM(fxMesa)) {
      /* see if we really need to update the unit */
      if (1/*fxMesa->TexState.Enabled[unit] != ctx->Texture.Unit[unit]._ReallyEnabled ||
          envMode != fxMesa->TexState.EnvMode[0] ||
          envMode == GL_COMBINE_EXT ||
          baseFormat != fxMesa->TexState.TexFormat[0]*/) {
         struct tdfx_texcombine_ext *otherEnv;
         if (!SetupTexEnvNapalm(ctx, GL_TRUE,
                                &ctx->Texture.Unit[unit], baseFormat,
                                &fxMesa->TexCombineExt[0])) {
            /* software fallback */
            FALLBACK(fxMesa, TDFX_FALLBACK_TEXTURE_ENV, GL_TRUE);
         }
         /* disable other unit */
         otherEnv = &fxMesa->TexCombineExt[1];
         otherEnv->Color.SourceA = GR_CMBX_ZERO;
         otherEnv->Color.ModeA = GR_FUNC_MODE_ZERO;
         otherEnv->Color.SourceB = GR_CMBX_ZERO;
         otherEnv->Color.ModeB = GR_FUNC_MODE_ZERO;
         otherEnv->Color.SourceC = GR_CMBX_ZERO;
         otherEnv->Color.InvertC = FXFALSE;
         otherEnv->Color.SourceD = GR_CMBX_ZERO;
         otherEnv->Color.InvertD = FXFALSE;
         otherEnv->Color.Shift = 0;
         otherEnv->Color.Invert = FXFALSE;
         otherEnv->Alpha.SourceA = GR_CMBX_ITALPHA;
         otherEnv->Alpha.ModeA = GR_FUNC_MODE_ZERO;
         otherEnv->Alpha.SourceB = GR_CMBX_ITALPHA;
         otherEnv->Alpha.ModeB = GR_FUNC_MODE_ZERO;
         otherEnv->Alpha.SourceC = GR_CMBX_ZERO;
         otherEnv->Alpha.InvertC = FXFALSE;
         otherEnv->Alpha.SourceD = GR_CMBX_ZERO;
         otherEnv->Alpha.InvertD = FXFALSE;
         otherEnv->Alpha.Shift = 0;
         otherEnv->Alpha.Invert = FXFALSE;

#if 0/*JJJ*/
         fxMesa->TexState.Enabled[unit] = ctx->Texture.Unit[unit]._ReallyEnabled;
         fxMesa->TexState.EnvMode[0] = envMode;
         fxMesa->TexState.TexFormat[0] = baseFormat;
         fxMesa->TexState.EnvMode[1] = 0;
         fxMesa->TexState.TexFormat[1] = 0;
#endif
      }
   }
   else {
      /* Voodoo3 */

      /* see if we really need to update the unit */
      if (1/*fxMesa->TexState.Enabled[unit] != ctx->Texture.Unit[unit]._ReallyEnabled ||
          envMode != fxMesa->TexState.EnvMode[0] ||
          envMode == GL_COMBINE_EXT ||
          baseFormat != fxMesa->TexState.TexFormat[0]*/) {
         if (!SetupSingleTexEnvVoodoo3(ctx, unit, envMode, baseFormat)) {
            /* software fallback */
            FALLBACK(fxMesa, TDFX_FALLBACK_TEXTURE_ENV, GL_TRUE);
         }
#if 0/*JJJ*/
         fxMesa->TexState.Enabled[unit] = ctx->Texture.Unit[unit]._ReallyEnabled;
         fxMesa->TexState.EnvMode[0] = envMode;
         fxMesa->TexState.TexFormat[0] = baseFormat;
         fxMesa->TexState.EnvMode[1] = 0;
         fxMesa->TexState.TexFormat[1] = 0;
#endif
      }
   }
}


static void
setupDoubleTMU(tdfxContextPtr 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

    const struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
    const struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData;
    const GLcontext *ctx = fxMesa->glCtx;
    tdfxTexInfo *ti0 = TDFX_TEXTURE_DATA(tObj0);
    tdfxTexInfo *ti1 = TDFX_TEXTURE_DATA(tObj1);
    GLuint tstate = 0;
    int tmu0 = 0, tmu1 = 1;

    if (shared->umaTexMemory) {
       if (!ti0->isInTM) {
          tdfxTMMoveInTM_NoLock(fxMesa, tObj0, TDFX_TMU0);
          assert(ti0->isInTM);
       }
       if (!ti1->isInTM) {
          tdfxTMMoveInTM_NoLock(fxMesa, tObj1, TDFX_TMU0);
          assert(ti1->isInTM);
       }
    }
    else {
       /* We shouldn't need to do this. There is something wrong with
          multitexturing when the TMUs are swapped. So, we're forcing
          them to always be loaded correctly. !!! */
       if (ti0->whichTMU == TDFX_TMU1)
           tdfxTMMoveOutTM_NoLock(fxMesa, tObj0);
       if (ti1->whichTMU == TDFX_TMU0)
           tdfxTMMoveOutTM_NoLock(fxMesa, tObj1);

       if (ti0->isInTM) {
           switch (ti0->whichTMU) {
           case TDFX_TMU0:
               tstate |= T0_IN_TMU0;
               break;
           case TDFX_TMU1:
               tstate |= T0_IN_TMU1;
               break;
           case TDFX_TMU_BOTH:
               tstate |= T0_IN_TMU0 | T0_IN_TMU1;
               break;
           case TDFX_TMU_SPLIT:
               tstate |= T0_NOT_IN_TMU;
               break;
           }
       }
       else
           tstate |= T0_NOT_IN_TMU;

       if (ti1->isInTM) {
           switch (ti1->whichTMU) {
           case TDFX_TMU0:
               tstate |= T1_IN_TMU0;
               break;
           case TDFX_TMU1:
               tstate |= T1_IN_TMU1;
               break;
           case TDFX_TMU_BOTH:
               tstate |= T1_IN_TMU0 | T1_IN_TMU1;
               break;
           case TDFX_TMU_SPLIT:
               tstate |= T1_NOT_IN_TMU;
               break;
           }
       }
       else
           tstate |= T1_NOT_IN_TMU;

       /* 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) {
              tdfxTMMoveInTM_NoLock(fxMesa, tObj1, TDFX_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 */
                      tdfxTMMoveInTM_NoLock(fxMesa, tObj1, TDFX_TMU1);
                   }
                   else {
                       tdfxTMMoveInTM_NoLock(fxMesa, tObj0, TDFX_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 */
                      tdfxTMMoveInTM_NoLock(fxMesa, tObj0, TDFX_TMU1);
                   }
                   else {
                       tdfxTMMoveInTM_NoLock(fxMesa, tObj1, TDFX_TMU0);
                   }
                   tmu0 = 1;
                   tmu1 = 0;
               }
               else {              /* Nothing is loaded */
                   tdfxTMMoveInTM_NoLock(fxMesa, tObj0, TDFX_TMU0);
                   tdfxTMMoveInTM_NoLock(fxMesa, tObj1, TDFX_TMU1);
                   /* tmu0 and tmu1 are setup */
               }
           }
       }
    }

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


    if (!ctx->Texture.SharedPalette) {
        if (ti0->info.format == GR_TEXFMT_P_8) {
            fxMesa->TexPalette.Type = ti0->paltype;
            fxMesa->TexPalette.Data = &(ti0->palette);
            fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_PALETTE;
        }
        else if (ti1->info.format == GR_TEXFMT_P_8) {
            fxMesa->TexPalette.Type = ti1->paltype;
            fxMesa->TexPalette.Data = &(ti1->palette);
            fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_PALETTE;
        }
        else {
            fxMesa->TexPalette.Data = NULL;
        }
    }

    /*
     * Setup Unit 0
     */
    assert(ti0->isInTM);
    assert(ti0->tm[tmu0]);
    fxMesa->TexSource[tmu0].StartAddress = ti0->tm[tmu0]->startAddr;
    fxMesa->TexSource[tmu0].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
    fxMesa->TexSource[tmu0].Info = &(ti0->info);
    fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_SOURCE;

    if (fxMesa->TexParams[tmu0].sClamp != ti0->sClamp ||
        fxMesa->TexParams[tmu0].tClamp != ti0->tClamp ||
        fxMesa->TexParams[tmu0].minFilt != ti0->minFilt ||
        fxMesa->TexParams[tmu0].magFilt != ti0->magFilt ||
        fxMesa->TexParams[tmu0].mmMode != ti0->mmMode ||
        fxMesa->TexParams[tmu0].LODblend != FXFALSE ||
        fxMesa->TexParams[tmu0].LodBias != ctx->Texture.Unit[tmu0].LodBias) {
       fxMesa->TexParams[tmu0].sClamp = ti0->sClamp;
       fxMesa->TexParams[tmu0].tClamp = ti0->tClamp;
       fxMesa->TexParams[tmu0].minFilt = ti0->minFilt;
       fxMesa->TexParams[tmu0].magFilt = ti0->magFilt;
       fxMesa->TexParams[tmu0].mmMode = ti0->mmMode;
       fxMesa->TexParams[tmu0].LODblend = FXFALSE;
       fxMesa->TexParams[tmu0].LodBias = ctx->Texture.Unit[tmu0].LodBias;
       fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_PARAMS;
    }

    /*
     * Setup Unit 1
     */
    if (shared->umaTexMemory) {
        ASSERT(ti1->isInTM);
        ASSERT(ti1->tm[0]);
        fxMesa->TexSource[tmu1].StartAddress = ti1->tm[0]->startAddr;
        fxMesa->TexSource[tmu1].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
        fxMesa->TexSource[tmu1].Info = &(ti1->info);
    }
    else {
        ASSERT(ti1->isInTM);
        ASSERT(ti1->tm[tmu1]);
        fxMesa->TexSource[tmu1].StartAddress = ti1->tm[tmu1]->startAddr;
        fxMesa->TexSource[tmu1].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
        fxMesa->TexSource[tmu1].Info = &(ti1->info);
    }

    if (fxMesa->TexParams[tmu1].sClamp != ti1->sClamp ||
        fxMesa->TexParams[tmu1].tClamp != ti1->tClamp ||
        fxMesa->TexParams[tmu1].minFilt != ti1->minFilt ||
        fxMesa->TexParams[tmu1].magFilt != ti1->magFilt ||
        fxMesa->TexParams[tmu1].mmMode != ti1->mmMode ||
        fxMesa->TexParams[tmu1].LODblend != FXFALSE ||
        fxMesa->TexParams[tmu1].LodBias != ctx->Texture.Unit[tmu1].LodBias) {
       fxMesa->TexParams[tmu1].sClamp = ti1->sClamp;
       fxMesa->TexParams[tmu1].tClamp = ti1->tClamp;
       fxMesa->TexParams[tmu1].minFilt = ti1->minFilt;
       fxMesa->TexParams[tmu1].magFilt = ti1->magFilt;
       fxMesa->TexParams[tmu1].mmMode = ti1->mmMode;
       fxMesa->TexParams[tmu1].LODblend = FXFALSE;
       fxMesa->TexParams[tmu1].LodBias = ctx->Texture.Unit[tmu1].LodBias;
       fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_PARAMS;
    }

    fxMesa->sScale0 = ti0->sScale;
    fxMesa->tScale0 = ti0->tScale;
    fxMesa->sScale1 = ti1->sScale;
    fxMesa->tScale1 = ti1->tScale;

#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 setupTextureDoubleTMU(GLcontext * ctx)
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   struct gl_texture_object *tObj0 = ctx->Texture.Unit[1]._Current;
   struct gl_texture_object *tObj1 = ctx->Texture.Unit[0]._Current;
   tdfxTexInfo *ti0 = TDFX_TEXTURE_DATA(tObj0);
   tdfxTexInfo *ti1 = TDFX_TEXTURE_DATA(tObj1);
   struct gl_texture_image *baseImage0 = tObj0->Image[0][tObj0->BaseLevel];
   struct gl_texture_image *baseImage1 = tObj1->Image[0][tObj1->BaseLevel];
#if 0/*JJJ*/
   const GLenum envMode0 = ctx->Texture.Unit[0].EnvMode;
   const GLenum envMode1 = ctx->Texture.Unit[1].EnvMode;
#endif

   if (baseImage0->Border > 0 || baseImage1->Border > 0) {
      FALLBACK(fxMesa, TDFX_FALLBACK_TEXTURE_BORDER, GL_TRUE);
      return;
   }

   setupDoubleTMU(fxMesa, tObj0, tObj1);

   if (ti0->reloadImages || ti1->reloadImages)
      fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_IMAGES;

   fxMesa->tmuSrc = TDFX_TMU_BOTH;

   if (TDFX_IS_NAPALM(fxMesa)) {
      /* Remember, Glide has its texture units numbered in backward
       * order compared to OpenGL.
       */
      GLboolean hw1 = GL_TRUE, hw2 = GL_TRUE;

      /* check if we really need to update glide unit 1 */
      if (1/*fxMesa->TexState.Enabled[0] != ctx->Texture.Unit[0]._ReallyEnabled ||
          envMode0 != fxMesa->TexState.EnvMode[1] ||
          envMode0 == GL_COMBINE_EXT ||
          baseImage0->Format != fxMesa->TexState.TexFormat[1] ||
          (fxMesa->Fallback & TDFX_FALLBACK_TEXTURE_ENV)*/) {
         hw1 = SetupTexEnvNapalm(ctx, GL_TRUE, &ctx->Texture.Unit[0],
                                baseImage0->_BaseFormat, &fxMesa->TexCombineExt[1]);
#if 0/*JJJ*/
         fxMesa->TexState.EnvMode[1] = envMode0;
         fxMesa->TexState.TexFormat[1] = baseImage0->_BaseFormat;
         fxMesa->TexState.Enabled[0] = ctx->Texture.Unit[0]._ReallyEnabled;
#endif
      }

      /* check if we really need to update glide unit 0 */
      if (1/*fxMesa->TexState.Enabled[1] != ctx->Texture.Unit[1]._ReallyEnabled ||
          envMode1 != fxMesa->TexState.EnvMode[0] ||
          envMode1 == GL_COMBINE_EXT ||
          baseImage1->_BaseFormat != fxMesa->TexState.TexFormat[0] ||
          (fxMesa->Fallback & TDFX_FALLBACK_TEXTURE_ENV)*/) {
         hw2 = SetupTexEnvNapalm(ctx, GL_FALSE, &ctx->Texture.Unit[1],
                                baseImage1->_BaseFormat, &fxMesa->TexCombineExt[0]);
#if 0/*JJJ*/
         fxMesa->TexState.EnvMode[0] = envMode1;
         fxMesa->TexState.TexFormat[0] = baseImage1->_BaseFormat;
         fxMesa->TexState.Enabled[1] = ctx->Texture.Unit[1]._ReallyEnabled;
#endif
      }


      if (!hw1 || !hw2) {
         FALLBACK(fxMesa, TDFX_FALLBACK_TEXTURE_ENV, GL_TRUE);
      }
   }
   else {
      int unit0, unit1;
      if ((ti0->whichTMU == TDFX_TMU1) || (ti1->whichTMU == TDFX_TMU0))
         unit0 = 1;
      else
         unit0 = 0;
      unit1 = 1 - unit0;

      if (1/*fxMesa->TexState.Enabled[0] != ctx->Texture.Unit[0]._ReallyEnabled ||
          fxMesa->TexState.Enabled[1] != ctx->Texture.Unit[1]._ReallyEnabled ||
          envMode0 != fxMesa->TexState.EnvMode[unit0] ||
          envMode0 == GL_COMBINE_EXT ||
          envMode1 != fxMesa->TexState.EnvMode[unit1] ||
          envMode1 == GL_COMBINE_EXT ||
          baseImage0->_BaseFormat != fxMesa->TexState.TexFormat[unit0] ||
          baseImage1->_BaseFormat != fxMesa->TexState.TexFormat[unit1] ||
          (fxMesa->Fallback & TDFX_FALLBACK_TEXTURE_ENV)*/) {

         if (!SetupDoubleTexEnvVoodoo3(ctx, unit0,
                         ctx->Texture.Unit[0].EnvMode, baseImage0->_BaseFormat,
                         ctx->Texture.Unit[1].EnvMode, baseImage1->_BaseFormat)) {
            FALLBACK(fxMesa, TDFX_FALLBACK_TEXTURE_ENV, GL_TRUE);
         }

#if 0/*JJJ*/
         fxMesa->TexState.EnvMode[unit0] = envMode0;
         fxMesa->TexState.TexFormat[unit0] = baseImage0->_BaseFormat;
         fxMesa->TexState.EnvMode[unit1] = envMode1;
         fxMesa->TexState.TexFormat[unit1] = baseImage1->_BaseFormat;
         fxMesa->TexState.Enabled[0] = ctx->Texture.Unit[0]._ReallyEnabled;
         fxMesa->TexState.Enabled[1] = ctx->Texture.Unit[1]._ReallyEnabled;
#endif
      }
   }
}


void
tdfxUpdateTextureState( GLcontext *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);

   FALLBACK(fxMesa, TDFX_FALLBACK_TEXTURE_BORDER, GL_FALSE);
   FALLBACK(fxMesa, TDFX_FALLBACK_TEXTURE_ENV, GL_FALSE);

   if (ctx->Texture.Unit[0]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT) &&
       ctx->Texture.Unit[1]._ReallyEnabled == 0) {
      LOCK_HARDWARE( fxMesa );  /* XXX remove locking eventually */
      setupTextureSingleTMU(ctx, 0);
      UNLOCK_HARDWARE( fxMesa );
   }
   else if (ctx->Texture.Unit[0]._ReallyEnabled == 0 && 
            ctx->Texture.Unit[1]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) {
      LOCK_HARDWARE( fxMesa );
      setupTextureSingleTMU(ctx, 1);
      UNLOCK_HARDWARE( fxMesa );
   }
   else if (ctx->Texture.Unit[0]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT) &&
            ctx->Texture.Unit[1]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) {
      LOCK_HARDWARE( fxMesa );
      setupTextureDoubleTMU(ctx);
      UNLOCK_HARDWARE( fxMesa );
   }
   else {
      /* disable hardware texturing */
      if (TDFX_IS_NAPALM(fxMesa)) {
         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;
      }
      else {
         /* Voodoo 3*/
         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->TexState.Enabled[0] = 0;
      fxMesa->TexState.Enabled[1] = 0;
      fxMesa->TexState.EnvMode[0] = 0;
      fxMesa->TexState.EnvMode[1] = 0;

      fxMesa->dirty |= TDFX_UPLOAD_COLOR_COMBINE;
      fxMesa->dirty |= TDFX_UPLOAD_ALPHA_COMBINE;

      if (ctx->Texture.Unit[0]._ReallyEnabled != 0 ||
          ctx->Texture.Unit[1]._ReallyEnabled != 0) {
         /* software texture (cube map, rect tex, etc */
         FALLBACK(fxMesa, TDFX_FALLBACK_TEXTURE_ENV, GL_TRUE);
      }
   }
}



/*
 * This is a special case of texture state update.
 * It's used when we've simply bound a new texture to a texture
 * unit and the new texture has the exact same attributes as the
 * previously bound texture.
 * This is very common in Quake3.
 */
void
tdfxUpdateTextureBinding( GLcontext *ctx )
{
   tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
   struct gl_texture_object *tObj0 = ctx->Texture.Unit[0]._Current;
   struct gl_texture_object *tObj1 = ctx->Texture.Unit[1]._Current;
   tdfxTexInfo *ti0 = TDFX_TEXTURE_DATA(tObj0);
   tdfxTexInfo *ti1 = TDFX_TEXTURE_DATA(tObj1);

    const struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
    const struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData;

   if (ti0) {
      fxMesa->sScale0 = ti0->sScale;
      fxMesa->tScale0 = ti0->tScale;
      if (ti0->info.format == GR_TEXFMT_P_8) {
         fxMesa->TexPalette.Type = ti0->paltype;
         fxMesa->TexPalette.Data = &(ti0->palette);
         fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_PALETTE;
      }
      else if (ti1 && ti1->info.format == GR_TEXFMT_P_8) {
         fxMesa->TexPalette.Type = ti1->paltype;
         fxMesa->TexPalette.Data = &(ti1->palette);
         fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_PALETTE;
      }
   }
   if (ti1) {
      fxMesa->sScale1 = ti1->sScale;
      fxMesa->tScale1 = ti1->tScale;
   }

   if (ctx->Texture.Unit[0]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT) &&
       ctx->Texture.Unit[0]._ReallyEnabled == 0) {
      /* Only unit 0 2D enabled */
      if (shared->umaTexMemory) {
         fxMesa->TexSource[0].StartAddress = ti0->tm[0]->startAddr;
         fxMesa->TexSource[0].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
         fxMesa->TexSource[0].Info = &(ti0->info);
      }
      else {
         if (ti0->LODblend && ti0->whichTMU == TDFX_TMU_SPLIT) {
            fxMesa->TexSource[0].StartAddress = ti0->tm[TDFX_TMU0]->startAddr;
            fxMesa->TexSource[0].EvenOdd = GR_MIPMAPLEVELMASK_ODD;
            fxMesa->TexSource[0].Info = &(ti0->info);
            fxMesa->TexSource[1].StartAddress = ti0->tm[TDFX_TMU1]->startAddr;
            fxMesa->TexSource[1].EvenOdd = GR_MIPMAPLEVELMASK_EVEN;
            fxMesa->TexSource[1].Info = &(ti0->info);
         }
         else {
            FxU32 tmu;
            if (ti0->whichTMU == TDFX_TMU_BOTH)
               tmu = TDFX_TMU0;
            else
               tmu = ti0->whichTMU;
            fxMesa->TexSource[0].Info = NULL;
            fxMesa->TexSource[1].Info = NULL;
            if (ti0->tm[tmu]) {
               fxMesa->TexSource[tmu].StartAddress = ti0->tm[tmu]->startAddr;
               fxMesa->TexSource[tmu].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
               fxMesa->TexSource[tmu].Info = &(ti0->info);
            }
         }
      }
   }
   else if (ctx->Texture.Unit[0]._ReallyEnabled == 0 && 
            ctx->Texture.Unit[0]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) {
      /* Only unit 1 2D enabled */
      if (shared->umaTexMemory) {
         fxMesa->TexSource[0].StartAddress = ti1->tm[0]->startAddr;
         fxMesa->TexSource[0].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
         fxMesa->TexSource[0].Info = &(ti1->info);
      }
   }
   else if (ctx->Texture.Unit[0]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT) && 
            ctx->Texture.Unit[0]._ReallyEnabled & (TEXTURE_1D_BIT|TEXTURE_2D_BIT)) {
      /* Both 2D enabled */
      if (shared->umaTexMemory) {
         const FxU32 tmu0 = 0, tmu1 = 1;
         fxMesa->TexSource[tmu0].StartAddress = ti0->tm[0]->startAddr;
         fxMesa->TexSource[tmu0].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
         fxMesa->TexSource[tmu0].Info = &(ti0->info);

         fxMesa->TexSource[tmu1].StartAddress = ti1->tm[0]->startAddr;
         fxMesa->TexSource[tmu1].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
         fxMesa->TexSource[tmu1].Info = &(ti1->info);
      }
      else {
         const FxU32 tmu0 = 0, tmu1 = 1;
         fxMesa->TexSource[tmu0].StartAddress = ti0->tm[tmu0]->startAddr;
         fxMesa->TexSource[tmu0].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
         fxMesa->TexSource[tmu0].Info = &(ti0->info);

         fxMesa->TexSource[tmu1].StartAddress = ti1->tm[tmu1]->startAddr;
         fxMesa->TexSource[tmu1].EvenOdd = GR_MIPMAPLEVELMASK_BOTH;
         fxMesa->TexSource[tmu1].Info = &(ti1->info);
      }
   }


   fxMesa->dirty |= TDFX_UPLOAD_TEXTURE_SOURCE;
}
