mesa/drivers: use _mesa_get_format_bytes()
[mesa.git] / src / mesa / drivers / dri / mga / mga_texstate.c
index fc1406cab9ea1a943fa810b09bc75643e4def872..8f78ab9bd4aaf33ad3f896e02e132591363fbe9b 100644 (file)
  *    Ian Romanick <idr@us.ibm.com>
  *    Keith Whitwell <keithw@tungstengraphics.com>
  */
-/* $XFree86:$ */
 
-#include "mm.h"
+#include <stdlib.h>
+#include "main/mm.h"
 #include "mgacontext.h"
 #include "mgatex.h"
 #include "mgaregs.h"
 #include "mgatris.h"
 #include "mgaioctl.h"
 
-#include "context.h"
-#include "enums.h"
-#include "macros.h"
-#include "imports.h"
+#include "main/context.h"
+#include "main/enums.h"
+#include "main/macros.h"
+#include "main/imports.h"
 
-#include "simple_list.h"
-#include "texformat.h"
+#include "main/simple_list.h"
+#include "main/texformat.h"
 
 #define MGA_USE_TABLE_FOR_FORMAT
 #ifdef MGA_USE_TABLE_FOR_FORMAT
 #define TMC_nr_tformat (MESA_FORMAT_YCBCR_REV + 1)
 static const unsigned TMC_tformat[ TMC_nr_tformat ] =
 {
-    [MESA_FORMAT_ARGB8888] = TMC_tformat_tw32 | TMC_takey_1 | TMC_tamask_0,
-    [MESA_FORMAT_RGB565]   = TMC_tformat_tw16 | TMC_takey_1 | TMC_tamask_0,
-    [MESA_FORMAT_ARGB4444] = TMC_tformat_tw12 | TMC_takey_1 | TMC_tamask_0,
-    [MESA_FORMAT_ARGB1555] = TMC_tformat_tw15 | TMC_takey_1 | TMC_tamask_0,
-    [MESA_FORMAT_CI8]      = TMC_tformat_tw8  | TMC_takey_1 | TMC_tamask_0,
-    [MESA_FORMAT_YCBCR]     = TMC_tformat_tw422uyvy | TMC_takey_1 | TMC_tamask_0,
-    [MESA_FORMAT_YCBCR_REV] = TMC_tformat_tw422 | TMC_takey_1 | TMC_tamask_0,
+    [MESA_FORMAT_ARGB8888] = TMC_tformat_tw32,
+    [MESA_FORMAT_RGB565]   = TMC_tformat_tw16,
+    [MESA_FORMAT_ARGB4444] = TMC_tformat_tw12,
+    [MESA_FORMAT_ARGB1555] = TMC_tformat_tw15,
+    [MESA_FORMAT_AL88]     = TMC_tformat_tw8al,
+    [MESA_FORMAT_I8]       = TMC_tformat_tw8a,
+    [MESA_FORMAT_CI8]      = TMC_tformat_tw8 ,
+    [MESA_FORMAT_YCBCR]     = TMC_tformat_tw422uyvy,
+    [MESA_FORMAT_YCBCR_REV] = TMC_tformat_tw422,
 };
 #endif
 
@@ -63,11 +65,11 @@ mgaSetTexImages( mgaContextPtr mmesa,
                 const struct gl_texture_object * tObj )
 {
     mgaTextureObjectPtr t = (mgaTextureObjectPtr) tObj->DriverData;
-    struct gl_texture_image *baseImage = tObj->Image[ tObj->BaseLevel ];
+    struct gl_texture_image *baseImage = tObj->Image[0][ tObj->BaseLevel ];
     GLint totalSize;
     GLint width, height;
     GLint i;
-    GLint firstLevel, lastLevel, numLevels;
+    GLint numLevels;
     GLint log2Width, log2Height;
     GLuint txformat = 0;
     GLint ofs;
@@ -81,6 +83,8 @@ mgaSetTexImages( mgaContextPtr mmesa,
        case MESA_FORMAT_RGB565:   txformat = TMC_tformat_tw16; break;
        case MESA_FORMAT_ARGB4444: txformat = TMC_tformat_tw12; break;
        case MESA_FORMAT_ARGB1555: txformat = TMC_tformat_tw15; break;
+       case MESA_FORMAT_AL88:     txformat = TMC_tformat_tw8al; break;
+       case MESA_FORMAT_I8:       txformat = TMC_tformat_tw8a; break;
        case MESA_FORMAT_CI8:      txformat = TMC_tformat_tw8;  break;
         case MESA_FORMAT_YCBCR:    txformat  = TMC_tformat_tw422uyvy; break;
         case MESA_FORMAT_YCBCR_REV: txformat = TMC_tformat_tw422; break;
@@ -101,61 +105,55 @@ mgaSetTexImages( mgaContextPtr mmesa,
 
 #endif /* MGA_USE_TABLE_FOR_FORMAT */
 
-   if (tObj->MinFilter == GL_NEAREST || tObj->MinFilter == GL_LINEAR) {
-      /* GL_NEAREST and GL_LINEAR only care about GL_TEXTURE_BASE_LEVEL.
-       */
-
-      firstLevel = lastLevel = tObj->BaseLevel;
+   driCalculateTextureFirstLastLevel( (driTextureObject *) t );
+   if (tObj->Target == GL_TEXTURE_RECTANGLE_NV) {
+      log2Width = 0;
+      log2Height = 0;
    } else {
-      /* Compute which mipmap levels we really want to send to the hardware.
-       * This depends on the base image size, GL_TEXTURE_MIN_LOD,
-       * GL_TEXTURE_MAX_LOD, GL_TEXTURE_BASE_LEVEL, and GL_TEXTURE_MAX_LEVEL.
-       * Yes, this looks overly complicated, but it's all needed.
-       */
-
-      firstLevel = tObj->BaseLevel + (GLint)(tObj->MinLod + 0.5);
-      firstLevel = MAX2(firstLevel, tObj->BaseLevel);
-      lastLevel = tObj->BaseLevel + (GLint)(tObj->MaxLod + 0.5);
-      lastLevel = MAX2(lastLevel, tObj->BaseLevel);
-      lastLevel = MIN2(lastLevel, tObj->BaseLevel + baseImage->MaxLog2);
-      lastLevel = MIN2(lastLevel, tObj->MaxLevel);
-      lastLevel = MAX2(firstLevel, lastLevel); /* need at least one level */
+      log2Width  = tObj->Image[0][t->base.firstLevel]->WidthLog2;
+      log2Height = tObj->Image[0][t->base.firstLevel]->HeightLog2;
    }
 
-   log2Width = tObj->Image[firstLevel]->WidthLog2;
-   log2Height = tObj->Image[firstLevel]->HeightLog2;
-   width = tObj->Image[firstLevel]->Width;
-   height = tObj->Image[firstLevel]->Height;
+   width = tObj->Image[0][t->base.firstLevel]->Width;
+   height = tObj->Image[0][t->base.firstLevel]->Height;
 
-   numLevels = MIN2( lastLevel - firstLevel + 1,
+   numLevels = MIN2( t->base.lastLevel - t->base.firstLevel + 1,
                      MGA_IS_G200(mmesa) ? G200_TEX_MAXLEVELS : G400_TEX_MAXLEVELS);
 
 
    totalSize = 0;
    for ( i = 0 ; i < numLevels ; i++ ) {
-      const struct gl_texture_image * const texImage = tObj->Image[i+firstLevel];
+      const struct gl_texture_image * const texImage = 
+         tObj->Image[0][ i + t->base.firstLevel ];
+      int size;
 
-      if ( (texImage == NULL)
-          || ((i != 0)
-              && ((texImage->Width < 8) || (texImage->Height < 8))) ) {
+      if (texImage == NULL)
         break;
-      }
+
+      size = texImage->Width * texImage->Height *
+         _mesa_get_format_bytes(baseImage->TexFormat->MesaFormat);
 
       t->offsets[i] = totalSize;
       t->base.dirty_images[0] |= (1<<i);
 
-      totalSize += ((MAX2( texImage->Width, 8 ) *
-                     MAX2( texImage->Height, 8 ) *
-                     baseImage->TexFormat->TexelBytes) + 31) & ~31;
-   }
+      /* All mipmaps must be 32-byte aligned */
+      totalSize += (size + 31) & ~31;
 
-   numLevels = i;
-   lastLevel = firstLevel + numLevels - 1;
+      /* Since G400 calculates the offsets in hardware
+       * it can't handle more than one < 32 byte mipmap.
+       *
+       * Further testing has indicated that it can't
+       * handle any < 32 byte mipmaps.
+       */
+      if (MGA_IS_G400( mmesa ) && size <= 32) {
+         i++;
+         break;
+      }
+   }
 
    /* save these values */
-   t->base.firstLevel = firstLevel;
-   t->base.lastLevel = lastLevel;
-
+   numLevels = i;
+   t->base.lastLevel = t->base.firstLevel + numLevels - 1;
    t->base.totalSize = totalSize;
 
    /* setup hardware register values */
@@ -170,17 +168,17 @@ mgaSetTexImages( mgaContextPtr mmesa,
     */
 
    t->setup.texctl |= TMC_tpitchlin_enable;
-   t->setup.texctl |= (width & (2048 - 1)) << TMC_tpitchext_SHIFT;
+   t->setup.texctl |= MGA_FIELD( TMC_tpitchext, width & (2048 - 1) );
 
 
    /* G400 specifies the number of mip levels in a strange way.  Since there
-    * are up to 12 levels, it requires 4 bits.  Three of the bits are at the
+    * are up to 11 levels, it requires 4 bits.  Three of the bits are at the
     * high end of TEXFILTER.  The other bit is in the middle.  Weird.
     */
-
+   numLevels--;
    t->setup.texfilter &= TF_mapnb_MASK & TF_mapnbhigh_MASK & TF_reserved_MASK;
-   t->setup.texfilter |= (((numLevels-1) & 0x07) << (TF_mapnb_SHIFT));
-   t->setup.texfilter |= (((numLevels-1) & 0x08) << (TF_mapnbhigh_SHIFT - 3));
+   t->setup.texfilter |= MGA_FIELD( TF_mapnb, numLevels & 0x7 );
+   t->setup.texfilter |= MGA_FIELD( TF_mapnbhigh, (numLevels >> 3) & 0x1 );
 
    /* warp texture registers */
    ofs = MGA_IS_G200(mmesa) ? 28 : 11;
@@ -203,168 +201,290 @@ mgaSetTexImages( mgaContextPtr mmesa,
 
 static void mgaUpdateTextureEnvG200( GLcontext *ctx, GLuint unit )
 {
+   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
    struct gl_texture_object *tObj = ctx->Texture.Unit[0]._Current;
-   mgaTextureObjectPtr t;
+   mgaTextureObjectPtr t = (mgaTextureObjectPtr) tObj->DriverData;
+   GLenum format = tObj->Image[0][tObj->BaseLevel]->_BaseFormat;
 
-   if (!tObj || !tObj->DriverData)
+   if (tObj != ctx->Texture.Unit[0].CurrentTex[TEXTURE_2D_INDEX] &&
+       tObj != ctx->Texture.Unit[0].CurrentTex[TEXTURE_RECT_INDEX])
       return;
 
-   t = (mgaTextureObjectPtr)tObj->DriverData;
 
-   t->setup.texctl2 &= ~TMC_decalblend_enable;
+   t->setup.texctl &= ~TMC_tmodulate_enable;
+   t->setup.texctl2 &= ~(TMC_decalblend_enable |
+                         TMC_idecal_enable |
+                         TMC_decaldis_enable);
 
    switch (ctx->Texture.Unit[0].EnvMode) {
    case GL_REPLACE:
-      t->setup.texctl &= ~TMC_tmodulate_enable;
+      if (format == GL_ALPHA)
+         t->setup.texctl2 |= TMC_idecal_enable;
+
+      if (format == GL_RGB || format == GL_LUMINANCE)
+         mmesa->hw.alpha_sel = AC_alphasel_diffused;
+      else
+         mmesa->hw.alpha_sel = AC_alphasel_fromtex;
       break;
+
    case GL_MODULATE:
       t->setup.texctl |= TMC_tmodulate_enable;
+
+      if (format == GL_ALPHA)
+         t->setup.texctl2 |= (TMC_idecal_enable |
+                              TMC_decaldis_enable);
+
+      if (format == GL_RGB || format == GL_LUMINANCE)
+         mmesa->hw.alpha_sel = AC_alphasel_diffused;
+      else
+         mmesa->hw.alpha_sel = AC_alphasel_modulated;
       break;
+
    case GL_DECAL:
-      t->setup.texctl &= ~TMC_tmodulate_enable;
-      t->setup.texctl2 |= TMC_decalblend_enable;
+      if (format == GL_RGB || format == GL_RGBA)
+         t->setup.texctl2 |= TMC_decalblend_enable;
+      else
+         t->setup.texctl2 |= TMC_idecal_enable;
+
+      mmesa->hw.alpha_sel = AC_alphasel_diffused;
       break;
+
    case GL_BLEND:
-      FALLBACK( ctx, MGA_FALLBACK_TEXTURE, GL_TRUE );
+      if (format == GL_ALPHA) {
+         t->setup.texctl2 |= TMC_idecal_enable;
+         mmesa->hw.alpha_sel = AC_alphasel_modulated;
+      } else {
+         t->texenv_fallback = GL_TRUE;
+      }
       break;
+
    default:
       break;
    }
 }
 
 
-#define MGA_DISABLE            0
-#define MGA_REPLACE            1
-#define MGA_MODULATE           2
-#define MGA_DECAL              3
-#define MGA_BLEND              4
-#define MGA_ADD                        5
-#define MGA_MAX_COMBFUNC       6
+#define MGA_REPLACE            0
+#define MGA_MODULATE           1
+#define MGA_DECAL              2
+#define MGA_ADD                        3
+#define MGA_MAX_COMBFUNC       4
 
 static const GLuint g400_color_combine[][MGA_MAX_COMBFUNC] =
 {
    /* Unit 0:
     */
    {
-      /* Disable combiner stage
-       */
-      (0),
-
       /* GL_REPLACE
+       * Cv = Cs
+       * Av = Af
        */
       (TD0_color_sel_arg1 |
        TD0_alpha_arg2_diffuse |
-       TD0_alpha_sel_arg2 ),
+       TD0_alpha_sel_arg2),
       
       /* GL_MODULATE
+       * Cv = Cf Cs
+       * Av = Af
        */
       (TD0_color_arg2_diffuse |
        TD0_color_sel_mul |
        TD0_alpha_arg2_diffuse |
-       TD0_alpha_sel_mul),
+       TD0_alpha_sel_arg2),
       
       /* GL_DECAL
+       * Cv = Cs
+       * Av = Af
        */
       (TD0_color_sel_arg1 |
        TD0_alpha_arg2_diffuse |
        TD0_alpha_sel_arg2),
       
-      /* GL_BLEND
-       */
-      (0),
-      
       /* GL_ADD
+       * Cv = Cf + Cs
+       * Av = Af
        */
       (TD0_color_arg2_diffuse |
        TD0_color_add_add |
        TD0_color_sel_add |
        TD0_alpha_arg2_diffuse |
-       TD0_alpha_sel_mul),
+       TD0_alpha_sel_arg2),
    },
    
    /* Unit 1:
     */
    {
-      /* Disable combiner stage
-       */
-      (0),
-       
       /* GL_REPLACE
+       * Cv = Cs
+       * Av = Ap
        */
       (TD0_color_sel_arg1 |
-       TD0_alpha_arg2_diffuse |
-       TD0_alpha_sel_arg2 ),
+       TD0_alpha_arg2_prevstage |
+       TD0_alpha_sel_arg2),
       
       /* GL_MODULATE
+       * Cv = Cp Cs
+       * Av = Ap
        */
       (TD0_color_arg2_prevstage |
-       TD0_color_alpha_prevstage |
        TD0_color_sel_mul |
        TD0_alpha_arg2_prevstage |
-       TD0_alpha_sel_mul),
+       TD0_alpha_sel_arg2),
 
       /* GL_DECAL
+       * Cv = Cs
+       * Av = Ap
        */
       (TD0_color_sel_arg1 |
        TD0_alpha_arg2_prevstage |
-       TD0_alpha_sel_arg2 ),
-      
-      /* GL_BLEND
-       */
-      (0),
+       TD0_alpha_sel_arg2),
       
       /* GL_ADD
+       * Cv = Cp + Cs
+       * Av = Ap
        */
       (TD0_color_arg2_prevstage |
-       TD0_color_alpha_prevstage |
        TD0_color_add_add |
        TD0_color_sel_add |
        TD0_alpha_arg2_prevstage |
-       TD0_alpha_sel_mul),
+       TD0_alpha_sel_arg2),
    },
 };
 
-static const GLuint g400_alpha_combine[][MGA_MAX_COMBFUNC] =
+static const GLuint g400_color_alpha_combine[][MGA_MAX_COMBFUNC] =
 {
    /* Unit 0:
     */
    {
-      /* Disable combiner stage
-       */
-      (0),
-
       /* GL_REPLACE
+       * Cv = Cs
+       * Av = As
        */
-      (TD0_color_sel_arg2 |
-       TD0_color_arg2_diffuse |
-       TD0_alpha_sel_arg1 ),
+      (TD0_color_sel_arg1 |
+       TD0_alpha_sel_arg1),
       
       /* GL_MODULATE
-       * FIXME: Is this correct?
+       * Cv = Cf Cs
+       * Av = Af As
        */
       (TD0_color_arg2_diffuse |
        TD0_color_sel_mul |
        TD0_alpha_arg2_diffuse |
        TD0_alpha_sel_mul),
-
+      
       /* GL_DECAL
+       * tmp = Cf ( 1 - As )
+       * Cv = tmp + Cs As
+       * Av = Af
        */
       (TD0_color_arg2_diffuse |
-       TD0_color_sel_arg2 |
+       TD0_color_alpha_currtex |
+       TD0_color_alpha1inv_enable |
+       TD0_color_arg1mul_alpha1 |
+       TD0_color_blend_enable |
+       TD0_color_arg1add_mulout |
+       TD0_color_arg2add_mulout |
+       TD0_color_add_add |
+       TD0_color_sel_add |
        TD0_alpha_arg2_diffuse |
        TD0_alpha_sel_arg2),
 
-      /* GL_BLEND
+      /* GL_ADD
+       * Cv = Cf + Cs
+       * Av = Af As
        */
       (TD0_color_arg2_diffuse |
+       TD0_color_add_add |
+       TD0_color_sel_add |
+       TD0_alpha_arg2_diffuse |
+       TD0_alpha_sel_mul),
+   },
+   
+   /* Unit 1:
+    */
+   {
+      /* GL_REPLACE
+       * Cv = Cs
+       * Av = As
+       */
+      (TD0_color_sel_arg1 |
+       TD0_alpha_sel_arg1),
+      
+      /* GL_MODULATE
+       * Cv = Cp Cs
+       * Av = Ap As
+       */
+      (TD0_color_arg2_prevstage |
        TD0_color_sel_mul |
+       TD0_alpha_arg2_prevstage |
+       TD0_alpha_sel_mul),
+
+      /* GL_DECAL
+       * tmp = Cp ( 1 - As )
+       * Cv = tmp + Cs As
+       * Av = Ap
+       */
+      (TD0_color_arg2_prevstage |
+       TD0_color_alpha_currtex |
+       TD0_color_alpha1inv_enable |
+       TD0_color_arg1mul_alpha1 |
+       TD0_color_blend_enable |
+       TD0_color_arg1add_mulout |
+       TD0_color_arg2add_mulout |
+       TD0_color_add_add |
+       TD0_color_sel_add |
+       TD0_alpha_arg2_prevstage |
+       TD0_alpha_sel_arg2),
+      
+      /* GL_ADD
+       * Cv = Cp + Cs
+       * Av = Ap As
+       */
+      (TD0_color_arg2_prevstage |
+       TD0_color_add_add |
+       TD0_color_sel_add |
+       TD0_alpha_arg2_prevstage |
+       TD0_alpha_sel_mul),
+   },
+};
+
+static const GLuint g400_alpha_combine[][MGA_MAX_COMBFUNC] =
+{
+   /* Unit 0:
+    */
+   {
+      /* GL_REPLACE
+       * Cv = Cf
+       * Av = As
+       */
+      (TD0_color_arg2_diffuse |
+       TD0_color_sel_arg2 |
+       TD0_alpha_sel_arg1),
+      
+      /* GL_MODULATE
+       * Cv = Cf
+       * Av = Af As
+       */
+      (TD0_color_arg2_diffuse |
+       TD0_color_sel_arg2 |
        TD0_alpha_arg2_diffuse |
        TD0_alpha_sel_mul),
 
+      /* GL_DECAL (undefined)
+       * Cv = Cf
+       * Av = Af
+       */
+      (TD0_color_arg2_diffuse |
+       TD0_color_sel_arg2 |
+       TD0_alpha_arg2_diffuse |
+       TD0_alpha_sel_arg2),
+
       /* GL_ADD
+       * Cv = Cf
+       * Av = Af As
        */
       (TD0_color_arg2_diffuse |
-       TD0_color_sel_mul |
+       TD0_color_sel_arg2 |
        TD0_alpha_arg2_diffuse |
        TD0_alpha_sel_mul),
    },
@@ -372,48 +492,139 @@ static const GLuint g400_alpha_combine[][MGA_MAX_COMBFUNC] =
    /* Unit 1:
     */
    {
-      /* Disable combiner stage
-       */
-      (0),
-
       /* GL_REPLACE
+       * Cv = Cp
+       * Av = As
        */
-      (TD0_color_sel_arg2 |
-       TD0_color_arg2_diffuse |
-       TD0_alpha_sel_arg1 ),
+      (TD0_color_arg2_prevstage |
+       TD0_color_sel_arg2 |
+       TD0_alpha_sel_arg1),
       
       /* GL_MODULATE
-       * FIXME: Is this correct?
+       * Cv = Cp
+       * Av = Ap As
        */
       (TD0_color_arg2_prevstage |
-       TD0_color_alpha_prevstage |
-       TD0_color_sel_mul |
+       TD0_color_sel_arg2 |
        TD0_alpha_arg2_prevstage |
        TD0_alpha_sel_mul),
 
-      /* GL_DECAL
+      /* GL_DECAL (undefined)
+       * Cv = Cp
+       * Av = Ap
        */
       (TD0_color_arg2_prevstage |
        TD0_color_sel_arg2 |
        TD0_alpha_arg2_prevstage |
        TD0_alpha_sel_arg2),
 
-      /* GL_BLEND
-       */
-      (TD0_color_arg2_diffuse |
-       TD0_color_sel_mul |
-       TD0_alpha_arg2_diffuse |
-       TD0_alpha_sel_mul),
-
       /* GL_ADD
+       * Cv = Cp
+       * Av = Ap As
        */
       (TD0_color_arg2_prevstage |
-       TD0_color_sel_mul |
+       TD0_color_sel_arg2 |
        TD0_alpha_arg2_prevstage |
        TD0_alpha_sel_mul),
    },
 };
 
+static GLboolean mgaUpdateTextureEnvBlend( GLcontext *ctx, int unit )
+{
+   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
+   const int source = mmesa->tmu_source[unit];
+   const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[source];
+   const struct gl_texture_object *tObj = texUnit->_Current;
+   GLuint *reg = ((GLuint *)&mmesa->setup.tdualstage0 + unit);
+   GLenum format = tObj->Image[0][tObj->BaseLevel]->_BaseFormat;
+
+   *reg = 0;
+
+   if (format == GL_ALPHA) {
+      /* Cv = Cf */
+      *reg |= (TD0_color_arg2_diffuse |
+               TD0_color_sel_arg2);
+      /* Av = Af As */
+      *reg |= (TD0_alpha_arg2_diffuse |
+               TD0_alpha_sel_mul);
+      return GL_TRUE;
+   }
+
+   /* C1 = Cf ( 1 - Cs ) */
+   *reg |= (TD0_color_arg1_inv_enable |
+            TD0_color_arg2_diffuse |
+            TD0_color_sel_mul);
+
+   if (format == GL_RGB || format == GL_LUMINANCE) {
+      /* A1 = Af */
+      *reg |= (TD0_alpha_arg2_diffuse |
+               TD0_alpha_sel_arg2);
+   } else
+   if (format == GL_RGBA || format == GL_LUMINANCE_ALPHA) {
+      /* A1 = Af As */
+      *reg |= (TD0_alpha_arg2_diffuse |
+               TD0_alpha_sel_mul);
+   } else
+   if (format == GL_INTENSITY) {
+      /* A1 = Af ( 1 - As ) */
+      *reg |= (TD0_alpha_arg1_inv_enable |
+               TD0_alpha_arg2_diffuse |
+               TD0_alpha_sel_mul);
+   }
+   
+   if (RGB_ZERO(mmesa->envcolor[source]) &&
+       (format != GL_INTENSITY || ALPHA_ZERO(mmesa->envcolor[source])))
+      return GL_TRUE; /* all done */
+
+   if (ctx->Texture._EnabledUnits == 0x03)
+      return GL_FALSE; /* need both units */
+
+   mmesa->force_dualtex = GL_TRUE;
+   reg = &mmesa->setup.tdualstage1;
+   *reg = 0;
+
+   if (RGB_ZERO(mmesa->envcolor[source])) {
+      /* Cv = C1 */
+      *reg |= (TD0_color_arg2_prevstage |
+               TD0_color_sel_arg2);
+   } else
+   if (RGB_ONE(mmesa->envcolor[source])) {
+      /* Cv = C1 + Cs */
+      *reg |= (TD0_color_arg2_prevstage |
+               TD0_color_add_add |
+               TD0_color_sel_add);
+   } else
+   if (RGBA_EQUAL(mmesa->envcolor[source])) {
+      /* Cv = C1 + Cc Cs */
+      *reg |= (TD0_color_arg2_prevstage |
+               TD0_color_alpha_fcol |
+               TD0_color_arg2mul_alpha2 |
+               TD0_color_arg1add_mulout |
+               TD0_color_add_add |
+               TD0_color_sel_add);
+
+      mmesa->setup.fcol = mmesa->envcolor[source];
+   } else {
+      return GL_FALSE;
+   }
+
+   if (format != GL_INTENSITY || ALPHA_ZERO(mmesa->envcolor[source])) {
+      /* Av = A1 */
+      *reg |= (TD0_alpha_arg2_prevstage |
+               TD0_alpha_sel_arg2);
+   } else
+   if (ALPHA_ONE(mmesa->envcolor[source])) {
+      /* Av = A1 + As */
+      *reg |= (TD0_alpha_arg2_prevstage |
+               TD0_alpha_add_enable |
+               TD0_alpha_sel_add);
+   } else {
+      return GL_FALSE;
+   }
+
+   return GL_TRUE;
+}
+
 static void mgaUpdateTextureEnvG400( GLcontext *ctx, GLuint unit )
 {
    mgaContextPtr mmesa = MGA_CONTEXT( ctx );
@@ -421,143 +632,96 @@ static void mgaUpdateTextureEnvG400( GLcontext *ctx, GLuint unit )
    const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[source];
    const struct gl_texture_object *tObj = texUnit->_Current;
    GLuint *reg = ((GLuint *)&mmesa->setup.tdualstage0 + unit);
-   GLenum format;
+   mgaTextureObjectPtr t = (mgaTextureObjectPtr) tObj->DriverData;
+   GLenum format = tObj->Image[0][tObj->BaseLevel]->_BaseFormat;
 
-   if ( tObj != ctx->Texture.Unit[source].Current2D || !tObj ) 
+   if (tObj != ctx->Texture.Unit[source].CurrentTex[TEXTURE_2D_INDEX] &&
+       tObj != ctx->Texture.Unit[source].CurrentTex[TEXTURE_RECT_INDEX])
       return;
 
-   format = tObj->Image[tObj->BaseLevel]->Format;
-
    switch (ctx->Texture.Unit[source].EnvMode) {
    case GL_REPLACE:
-      if (format == GL_RGB || format == GL_LUMINANCE) {
-        *reg = g400_color_combine[unit][MGA_REPLACE];
-      }
-      else if (format == GL_ALPHA) {
+      if (format == GL_ALPHA) {
          *reg = g400_alpha_combine[unit][MGA_REPLACE];
-      }
-      else {
-         *reg = (TD0_color_sel_arg1 |
-                 TD0_alpha_sel_arg1 );
+      } else if (format == GL_RGB || format == GL_LUMINANCE) {
+         *reg = g400_color_combine[unit][MGA_REPLACE];
+      } else {
+         *reg = g400_color_alpha_combine[unit][MGA_REPLACE];
       }
       break;
 
    case GL_MODULATE:
-      *reg = g400_color_combine[unit][MGA_MODULATE];
+      if (format == GL_ALPHA) {
+         *reg = g400_alpha_combine[unit][MGA_MODULATE];
+      } else if (format == GL_RGB || format == GL_LUMINANCE) {
+         *reg = g400_color_combine[unit][MGA_MODULATE];
+      } else {
+         *reg = g400_color_alpha_combine[unit][MGA_MODULATE];
+      }
       break;
+
    case GL_DECAL:
       if (format == GL_RGB) {
-        *reg = g400_color_combine[unit][MGA_DECAL];
+         *reg = g400_color_combine[unit][MGA_DECAL];
+      } else if (format == GL_RGBA) {
+         *reg = g400_color_alpha_combine[unit][MGA_DECAL];
+         if (ctx->Texture._EnabledUnits != 0x03) {
+            /* Linear blending mode needs dual texturing enabled */
+            *(reg+1) = (TD0_color_arg2_prevstage |
+                        TD0_color_sel_arg2 |
+                        TD0_alpha_arg2_prevstage |
+                        TD0_alpha_sel_arg2);
+            mmesa->force_dualtex = GL_TRUE;
+         }
+      } else {
+         /* Undefined */
+         *reg = g400_alpha_combine[unit][MGA_DECAL];
       }
-      else if ( format == GL_RGBA ) {
-#if 0
+      break;
+
+   case GL_ADD:
+      if (format == GL_ALPHA) {
+         *reg = g400_alpha_combine[unit][MGA_ADD];
+      } else if (format == GL_RGB || format == GL_LUMINANCE) {
+         *reg = g400_color_combine[unit][MGA_ADD];
+      } else if (format == GL_RGBA || format == GL_LUMINANCE_ALPHA) {
+         *reg = g400_color_alpha_combine[unit][MGA_ADD];
+      } else if (format == GL_INTENSITY) {
+         /* Cv = Cf + Cs
+          * Av = Af + As
+          */
          if (unit == 0) {
-            /* this doesn't work */
             *reg = (TD0_color_arg2_diffuse |
-                    TD0_color_alpha_currtex |
-                    TD0_color_alpha2inv_enable |
-                    TD0_color_arg2mul_alpha2 |
-                    TD0_color_arg1mul_alpha1 |
-                    TD0_color_blend_enable |
-                    TD0_color_arg1add_mulout |
-                    TD0_color_arg2add_mulout |
                     TD0_color_add_add |
-                    TD0_color_sel_mul |
+                    TD0_color_sel_add |
                     TD0_alpha_arg2_diffuse |
-                    TD0_alpha_sel_arg2 );
-         }
-         else {
+                    TD0_alpha_add_enable |
+                    TD0_alpha_sel_add);
+         else {
             *reg = (TD0_color_arg2_prevstage |
-                    TD0_color_alpha_currtex |
-                    TD0_color_alpha2inv_enable |
-                    TD0_color_arg2mul_alpha2 |
-                    TD0_color_arg1mul_alpha1 |
                     TD0_color_add_add |
                     TD0_color_sel_add |
                     TD0_alpha_arg2_prevstage |
-                    TD0_alpha_sel_arg2 );
+                    TD0_alpha_add_enable |
+                    TD0_alpha_sel_add);
          }
-#else
-         /* s/w fallback, pretty sure we can't do in h/w */
-        FALLBACK( ctx, MGA_FALLBACK_TEXTURE, GL_TRUE );
-        if ( MGA_DEBUG & DEBUG_VERBOSE_FALLBACK )
-           fprintf( stderr, "FALLBACK: GL_DECAL RGBA texture, unit=%d\n",
-                    unit );
-#endif
-      }
-      else {
-       *reg = g400_alpha_combine[unit][MGA_DECAL];
       }
       break;
 
-   case GL_ADD:
-     if (format == GL_INTENSITY) {
-       if (unit == 0) {
-          *reg = ( TD0_color_arg2_diffuse |
-                  TD0_color_add_add |
-                  TD0_color_sel_add |
-                  TD0_alpha_arg2_diffuse |
-                  TD0_alpha_add_enable |
-                  TD0_alpha_sel_add);
-       }
-       else {
-          *reg = ( TD0_color_arg2_prevstage |
-                  TD0_color_add_add |
-                  TD0_color_sel_add |
-                  TD0_alpha_arg2_prevstage |
-                  TD0_alpha_add_enable |
-                  TD0_alpha_sel_add);
-       }
-     }      
-     else if (format == GL_ALPHA) {
-       *reg = g400_alpha_combine[unit][MGA_ADD];
-     }
-     else {
-       *reg = g400_color_combine[unit][MGA_ADD];
-     }
-     break;
-
    case GL_BLEND:
-      if (format == GL_ALPHA) {
-        *reg = g400_alpha_combine[unit][MGA_BLEND];
-      }
-      else {
-        FALLBACK( ctx, MGA_FALLBACK_TEXTURE, GL_TRUE );
-        if ( MGA_DEBUG & DEBUG_VERBOSE_FALLBACK )
-           fprintf( stderr, "FALLBACK: GL_BLEND envcolor=0x%08x\n",
-                    mmesa->envcolor );
-
-         /* Do singletexture GL_BLEND with 'all ones' env-color
-          * by using both texture units.  Multitexture gl_blend
-          * is a fallback.
-          */
-         if (unit == 0) {
-            /* Part 1: R1 = Rf ( 1 - Rt )
-             *         A1 = Af At
-             */
-            *reg = ( TD0_color_arg2_diffuse |
-                     TD0_color_arg1_inv_enable |
-                     TD0_color_sel_mul |
-                     TD0_alpha_arg2_diffuse |
-                     TD0_alpha_sel_arg1);
-         } else {
-            /* Part 2: R2 = R1 + Rt
-             *         A2 = A1
-             */
-            *reg = ( TD0_color_arg2_prevstage |
-                     TD0_color_add_add |
-                     TD0_color_sel_add |
-                     TD0_alpha_arg2_prevstage |
-                     TD0_alpha_sel_arg2);
-         }
-      }
+      if (!mgaUpdateTextureEnvBlend(ctx, unit))
+         t->texenv_fallback = GL_TRUE;
+      break;
+
+   case GL_COMBINE:
+      if (!mgaUpdateTextureEnvCombine(ctx, unit))
+         t->texenv_fallback = GL_TRUE;
       break;
    default:
       break;
    }
 }
 
-
 static void disable_tex( GLcontext *ctx, int unit )
 {
    mgaContextPtr mmesa = MGA_CONTEXT( ctx );
@@ -573,7 +737,7 @@ static void disable_tex( GLcontext *ctx, int unit )
       mmesa->CurrentTexObj[unit] = NULL;
    }
 
-   if ( unit != 0 ) {
+   if ( unit != 0 && !mmesa->force_dualtex ) {
       mmesa->setup.tdualstage1 = mmesa->setup.tdualstage0;
    }
 
@@ -586,7 +750,7 @@ static void disable_tex( GLcontext *ctx, int unit )
    mmesa->dirty |= MGA_UPLOAD_CONTEXT | (MGA_UPLOAD_TEX0 << unit);
 }
 
-static GLboolean enable_tex_2d( GLcontext *ctx, int unit )
+static GLboolean enable_tex( GLcontext *ctx, int unit )
 {
    mgaContextPtr mmesa = MGA_CONTEXT(ctx);
    const int source = mmesa->tmu_source[unit];
@@ -616,7 +780,7 @@ static GLboolean update_tex_common( GLcontext *ctx, int unit )
    mgaTextureObjectPtr t = (mgaTextureObjectPtr) tObj->DriverData;
 
    /* Fallback if there's a texture border */
-   if ( tObj->Image[tObj->BaseLevel]->Border > 0 ) {
+   if ( tObj->Image[0][tObj->BaseLevel]->Border > 0 ) {
       return GL_FALSE;
    }
 
@@ -644,10 +808,13 @@ static GLboolean update_tex_common( GLcontext *ctx, int unit )
       mmesa->setup.tdualstage1 = mmesa->setup.tdualstage0;
    }
 
-   t->setup.texctl2 &= TMC_dualtex_MASK;
-   if (ctx->Texture._EnabledUnits == 0x03) {
-      t->setup.texctl2 |= TMC_dualtex_enable;
-   }
+   t->texenv_fallback = GL_FALSE;
+
+   /* Set this before mgaUpdateTextureEnvG400() since
+    * GL_ARB_texture_env_crossbar may have to disable texturing.
+    */
+   mmesa->setup.dwgctl &= DC_opcod_MASK;
+   mmesa->setup.dwgctl |= DC_opcod_texture_trap;
 
    /* FIXME: The Radeon has some cached state so that it can avoid calling
     * FIXME: UpdateTextureEnv in some cases.  Is that possible here?
@@ -663,30 +830,18 @@ static GLboolean update_tex_common( GLcontext *ctx, int unit )
 
       mgaUpdateTextureEnvG400( ctx, unit );
    } else {
-      mmesa->hw.alpha_sel = 0;
-      switch (ctx->Texture.Unit[0].EnvMode) {
-      case GL_DECAL:
-        mmesa->hw.alpha_sel |= AC_alphasel_diffused;
-      case GL_REPLACE:
-        mmesa->hw.alpha_sel |= AC_alphasel_fromtex;
-        break;
-      case GL_BLEND:
-      case GL_MODULATE:
-        mmesa->hw.alpha_sel |= AC_alphasel_modulated;
-        break;
-      default:
-        break;
-      }
-
       mgaUpdateTextureEnvG200( ctx, unit );
    }
 
-   mmesa->setup.dwgctl &= DC_opcod_MASK;
-   mmesa->setup.dwgctl |= DC_opcod_texture_trap;
+   t->setup.texctl2 &= TMC_dualtex_MASK;
+   if (ctx->Texture._EnabledUnits == 0x03 || mmesa->force_dualtex) {
+      t->setup.texctl2 |= TMC_dualtex_enable;
+   }
+
    mmesa->dirty |= MGA_UPLOAD_CONTEXT | (MGA_UPLOAD_TEX0 << unit);
 
    FALLBACK( ctx, MGA_FALLBACK_BORDER_MODE, t->border_fallback );
-   return !t->border_fallback;
+   return !t->border_fallback && !t->texenv_fallback;
 }
 
 
@@ -697,8 +852,9 @@ static GLboolean updateTextureUnit( GLcontext *ctx, int unit )
    const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[source];
 
 
-   if ( texUnit->_ReallyEnabled == TEXTURE_2D_BIT) {
-      return(enable_tex_2d( ctx, unit ) &&
+   if ( texUnit->_ReallyEnabled == TEXTURE_2D_BIT ||
+        texUnit->_ReallyEnabled == TEXTURE_RECT_BIT ) {
+      return(enable_tex( ctx, unit ) &&
             update_tex_common( ctx, unit ));
    }
    else if ( texUnit->_ReallyEnabled ) {
@@ -718,6 +874,8 @@ void mgaUpdateTextureState( GLcontext *ctx )
    GLboolean ok;
    unsigned  i;
 
+   mmesa->force_dualtex = GL_FALSE;
+   mmesa->fcol_used = GL_FALSE;
 
    /* This works around a quirk with the MGA hardware.  If only OpenGL 
     * TEXTURE1 is enabled, then the hardware TEXTURE0 must be used.  The
@@ -740,8 +898,4 @@ void mgaUpdateTextureState( GLcontext *ctx )
    }
 
    FALLBACK( ctx, MGA_FALLBACK_TEXTURE, !ok );
-   
-   /* FIXME: I believe that ChooseVertexState should be called here instead of
-    * FIXME: in mgaDDValidateState.
-    */
 }