softpipe: shortcircuit repeated lookups of the same tile
[mesa.git] / src / gallium / drivers / softpipe / sp_tex_sample.c
index ed150527e2d4d7081e3600b919be539e41e88816..46c56b0c835e67b135fc662bccb9e868e8c27ea6 100644 (file)
@@ -2,6 +2,7 @@
  * 
  * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
  * All Rights Reserved.
+ * Copyright 2008 VMware, Inc.  All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the
  */
 
 #include "sp_context.h"
-#include "sp_headers.h"
+#include "sp_quad.h"
 #include "sp_surface.h"
+#include "sp_texture.h"
 #include "sp_tex_sample.h"
 #include "sp_tile_cache.h"
 #include "pipe/p_context.h"
 #include "pipe/p_defines.h"
-#include "pipe/p_util.h"
-#include "tgsi/tgsi_exec.h"
+#include "util/u_math.h"
+#include "util/u_memory.h"
+
 
 
 /*
  * Also note, FRAC(x) doesn't truly return the fractional part of x for x < 0.
  * Instead, if x < 0 then FRAC(x) = 1 - true_frac(x).
  */
-#define FRAC(f)  ((f) - ifloor(f))
+#define FRAC(f)  ((f) - util_ifloor(f))
 
 
 /**
  * Linear interpolation macro
  */
-#define LERP(T, A, B)  ( (A) + (T) * ((B) - (A)) )
+static INLINE float
+lerp(float a, float v0, float v1)
+{
+   return v0 + a * (v1 - v0);
+}
 
 
 /**
@@ -71,12 +78,27 @@ static INLINE float
 lerp_2d(float a, float b,
         float v00, float v10, float v01, float v11)
 {
-   const float temp0 = LERP(a, v00, v10);
-   const float temp1 = LERP(a, v01, v11);
-   return LERP(b, temp0, temp1);
+   const float temp0 = lerp(a, v00, v10);
+   const float temp1 = lerp(a, v01, v11);
+   return lerp(b, temp0, temp1);
 }
 
 
+/**
+ * As above, but 3D interpolation of 8 values.
+ */
+static INLINE float
+lerp_3d(float a, float b, float c,
+        float v000, float v100, float v010, float v110,
+        float v001, float v101, float v011, float v111)
+{
+   const float temp0 = lerp_2d(a, b, v000, v100, v010, v110);
+   const float temp1 = lerp_2d(a, b, v001, v101, v011, v111);
+   return lerp(c, temp0, temp1);
+}
+
+
+
 /**
  * If A is a signed integer, A % B doesn't give the right value for A < 0
  * (in terms of texture repeat).  Just casting to unsigned fixes that.
@@ -85,250 +107,275 @@ lerp_2d(float a, float b,
 
 
 /**
- * Apply texture coord wrapping mode and return integer texture index.
+ * Apply texture coord wrapping mode and return integer texture indexes
+ * for a vector of four texcoords (S or T or P).
  * \param wrapMode  PIPE_TEX_WRAP_x
- * \param s  the texcoord
+ * \param s  the incoming texcoords
  * \param size  the texture image size
+ * \param icoord  returns the integer texcoords
  * \return  integer texture index
  */
-static INLINE int
-nearest_texcoord(unsigned wrapMode, float s, unsigned size)
+static INLINE void
+nearest_texcoord_4(unsigned wrapMode, const float s[4], unsigned size,
+                   int icoord[4])
 {
-   int i;
+   uint ch;
    switch (wrapMode) {
    case PIPE_TEX_WRAP_REPEAT:
       /* s limited to [0,1) */
       /* i limited to [0,size-1] */
-      i = ifloor(s * size);
-      i = REMAINDER(i, size);
-      return i;
+      for (ch = 0; ch < 4; ch++) {
+         int i = util_ifloor(s[ch] * size);
+         icoord[ch] = REMAINDER(i, size);
+      }
+      return;
    case PIPE_TEX_WRAP_CLAMP:
       /* s limited to [0,1] */
       /* i limited to [0,size-1] */
-      if (s <= 0.0F)
-         i = 0;
-      else if (s >= 1.0F)
-         i = size - 1;
-      else
-         i = ifloor(s * size);
-      return i;
+      for (ch = 0; ch < 4; ch++) {
+         if (s[ch] <= 0.0F)
+            icoord[ch] = 0;
+         else if (s[ch] >= 1.0F)
+            icoord[ch] = size - 1;
+         else
+            icoord[ch] = util_ifloor(s[ch] * size);
+      }
+      return;
    case PIPE_TEX_WRAP_CLAMP_TO_EDGE:
       {
          /* s limited to [min,max] */
          /* i limited to [0, size-1] */
          const float min = 1.0F / (2.0F * size);
          const float max = 1.0F - min;
-         if (s < min)
-            i = 0;
-         else if (s > max)
-            i = size - 1;
-         else
-            i = ifloor(s * size);
+         for (ch = 0; ch < 4; ch++) {
+            if (s[ch] < min)
+               icoord[ch] = 0;
+            else if (s[ch] > max)
+               icoord[ch] = size - 1;
+            else
+               icoord[ch] = util_ifloor(s[ch] * size);
+         }
       }
-      return i;
+      return;
    case PIPE_TEX_WRAP_CLAMP_TO_BORDER:
       {
          /* s limited to [min,max] */
          /* i limited to [-1, size] */
          const float min = -1.0F / (2.0F * size);
          const float max = 1.0F - min;
-         if (s <= min)
-            i = -1;
-         else if (s >= max)
-            i = size;
-         else
-            i = ifloor(s * size);
+         for (ch = 0; ch < 4; ch++) {
+            if (s[ch] <= min)
+               icoord[ch] = -1;
+            else if (s[ch] >= max)
+               icoord[ch] = size;
+            else
+               icoord[ch] = util_ifloor(s[ch] * size);
+         }
       }
-      return i;
+      return;
    case PIPE_TEX_WRAP_MIRROR_REPEAT:
       {
          const float min = 1.0F / (2.0F * size);
          const float max = 1.0F - min;
-         const int flr = ifloor(s);
-         float u;
-         if (flr & 1)
-            u = 1.0F - (s - (float) flr);
-         else
-            u = s - (float) flr;
-         if (u < min)
-            i = 0;
-         else if (u > max)
-            i = size - 1;
-         else
-            i = ifloor(u * size);
+         for (ch = 0; ch < 4; ch++) {
+            const int flr = util_ifloor(s[ch]);
+            float u;
+            if (flr & 1)
+               u = 1.0F - (s[ch] - (float) flr);
+            else
+               u = s[ch] - (float) flr;
+            if (u < min)
+               icoord[ch] = 0;
+            else if (u > max)
+               icoord[ch] = size - 1;
+            else
+               icoord[ch] = util_ifloor(u * size);
+         }
       }
-      return i;
+      return;
    case PIPE_TEX_WRAP_MIRROR_CLAMP:
-      {
+      for (ch = 0; ch < 4; ch++) {
          /* s limited to [0,1] */
          /* i limited to [0,size-1] */
-         const float u = FABSF(s);
+         const float u = fabsf(s[ch]);
          if (u <= 0.0F)
-            i = 0;
+            icoord[ch] = 0;
          else if (u >= 1.0F)
-            i = size - 1;
+            icoord[ch] = size - 1;
          else
-            i = ifloor(u * size);
+            icoord[ch] = util_ifloor(u * size);
       }
-      return i;
+      return;
    case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE:
       {
          /* s limited to [min,max] */
          /* i limited to [0, size-1] */
          const float min = 1.0F / (2.0F * size);
          const float max = 1.0F - min;
-         const float u = FABSF(s);
-         if (u < min)
-            i = 0;
-         else if (u > max)
-            i = size - 1;
-         else
-            i = ifloor(u * size);
+         for (ch = 0; ch < 4; ch++) {
+            const float u = fabsf(s[ch]);
+            if (u < min)
+               icoord[ch] = 0;
+            else if (u > max)
+               icoord[ch] = size - 1;
+            else
+               icoord[ch] = util_ifloor(u * size);
+         }
       }
-      return i;
+      return;
    case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER:
       {
          /* s limited to [min,max] */
          /* i limited to [0, size-1] */
          const float min = -1.0F / (2.0F * size);
          const float max = 1.0F - min;
-         const float u = FABSF(s);
-         if (u < min)
-            i = -1;
-         else if (u > max)
-            i = size;
-         else
-            i = ifloor(u * size);
+         for (ch = 0; ch < 4; ch++) {
+            const float u = fabsf(s[ch]);
+            if (u < min)
+               icoord[ch] = -1;
+            else if (u > max)
+               icoord[ch] = size;
+            else
+               icoord[ch] = util_ifloor(u * size);
+         }
       }
-      return i;
+      return;
    default:
       assert(0);
-      return 0;
    }
 }
 
 
 /**
- * Used to compute texel locations for linear sampling.
+ * Used to compute texel locations for linear sampling for four texcoords.
  * \param wrapMode  PIPE_TEX_WRAP_x
- * \param s  the texcoord
+ * \param s  the texcoords
  * \param size  the texture image size
- * \param i0  returns first texture index
- * \param i1  returns second texture index (usually *i0 + 1)
- * \param a  returns blend factor/weight between texture indexes
+ * \param icoord0  returns first texture indexes
+ * \param icoord1  returns second texture indexes (usually icoord0 + 1)
+ * \param w  returns blend factor/weight between texture indexes
+ * \param icoord  returns the computed integer texture coords
  */
 static INLINE void
-linear_texcoord(unsigned wrapMode, float s, unsigned size,
-                int *i0, int *i1, float *a)
+linear_texcoord_4(unsigned wrapMode, const float s[4], unsigned size,
+                  int icoord0[4], int icoord1[4], float w[4])
 {
-   float u;
+   uint ch;
+
    switch (wrapMode) {
    case PIPE_TEX_WRAP_REPEAT:
-      u = s * size - 0.5F;
-      *i0 = REMAINDER(ifloor(u), size);
-      *i1 = REMAINDER(*i0 + 1, size);
-      break;
+      for (ch = 0; ch < 4; ch++) {
+         float u = s[ch] * size - 0.5F;
+         icoord0[ch] = REMAINDER(util_ifloor(u), size);
+         icoord1[ch] = REMAINDER(icoord0[ch] + 1, size);
+         w[ch] = FRAC(u);
+      }
+      break;;
    case PIPE_TEX_WRAP_CLAMP:
-      if (s <= 0.0F)
-         u = 0.0F;
-      else if (s >= 1.0F)
-         u = (float) size;
-      else
-         u = s * size;
-      u -= 0.5F;
-      *i0 = ifloor(u);
-      *i1 = *i0 + 1;
-      break;
+      for (ch = 0; ch < 4; ch++) {
+         float u = CLAMP(s[ch], 0.0F, 1.0F);
+         u = u * size - 0.5f;
+         icoord0[ch] = util_ifloor(u);
+         icoord1[ch] = icoord0[ch] + 1;
+         w[ch] = FRAC(u);
+      }
+      break;;
    case PIPE_TEX_WRAP_CLAMP_TO_EDGE:
-      if (s <= 0.0F)
-         u = 0.0F;
-      else if (s >= 1.0F)
-         u = (float) size;
-      else
-         u = s * size;
-      u -= 0.5F;
-      *i0 = ifloor(u);
-      *i1 = *i0 + 1;
-      if (*i0 < 0)
-         *i0 = 0;
-      if (*i1 >= (int) size)
-         *i1 = size - 1;
-      break;
+      for (ch = 0; ch < 4; ch++) {
+         float u = CLAMP(s[ch], 0.0F, 1.0F);
+         u = u * size - 0.5f;
+         icoord0[ch] = util_ifloor(u);
+         icoord1[ch] = icoord0[ch] + 1;
+         if (icoord0[ch] < 0)
+            icoord0[ch] = 0;
+         if (icoord1[ch] >= (int) size)
+            icoord1[ch] = size - 1;
+         w[ch] = FRAC(u);
+      }
+      break;;
    case PIPE_TEX_WRAP_CLAMP_TO_BORDER:
       {
          const float min = -1.0F / (2.0F * size);
          const float max = 1.0F - min;
-         if (s <= min)
-            u = min * size;
-         else if (s >= max)
-            u = max * size;
-         else
-            u = s * size;
-         u -= 0.5F;
-         *i0 = ifloor(u);
-         *i1 = *i0 + 1;
+         for (ch = 0; ch < 4; ch++) {
+            float u = CLAMP(s[ch], min, max);
+            u = u * size - 0.5f;
+            icoord0[ch] = util_ifloor(u);
+            icoord1[ch] = icoord0[ch] + 1;
+            w[ch] = FRAC(u);
+         }
       }
-      break;
+      break;;
    case PIPE_TEX_WRAP_MIRROR_REPEAT:
-      {
-         const int flr = ifloor(s);
+      for (ch = 0; ch < 4; ch++) {
+         const int flr = util_ifloor(s[ch]);
+         float u;
          if (flr & 1)
-            u = 1.0F - (s - (float) flr);
+            u = 1.0F - (s[ch] - (float) flr);
          else
-            u = s - (float) flr;
-         u = (u * size) - 0.5F;
-         *i0 = ifloor(u);
-         *i1 = *i0 + 1;
-         if (*i0 < 0)
-            *i0 = 0;
-         if (*i1 >= (int) size)
-            *i1 = size - 1;
+            u = s[ch] - (float) flr;
+         u = u * size - 0.5F;
+         icoord0[ch] = util_ifloor(u);
+         icoord1[ch] = icoord0[ch] + 1;
+         if (icoord0[ch] < 0)
+            icoord0[ch] = 0;
+         if (icoord1[ch] >= (int) size)
+            icoord1[ch] = size - 1;
+         w[ch] = FRAC(u);
       }
-      break;
+      break;;
    case PIPE_TEX_WRAP_MIRROR_CLAMP:
-      u = FABSF(s);
-      if (u >= 1.0F)
-         u = (float) size;
-      else
-         u *= size;
-      u -= 0.5F;
-      *i0 = ifloor(u);
-      *i1 = *i0 + 1;
-      break;
+      for (ch = 0; ch < 4; ch++) {
+         float u = fabsf(s[ch]);
+         if (u >= 1.0F)
+            u = (float) size;
+         else
+            u *= size;
+         u -= 0.5F;
+         icoord0[ch] = util_ifloor(u);
+         icoord1[ch] = icoord0[ch] + 1;
+         w[ch] = FRAC(u);
+      }
+      break;;
    case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE:
-      u = FABSF(s);
-      if (u >= 1.0F)
-         u = (float) size;
-      else
-         u *= size;
-      u -= 0.5F;
-      *i0 = ifloor(u);
-      *i1 = *i0 + 1;
-      if (*i0 < 0)
-         *i0 = 0;
-      if (*i1 >= (int) size)
-         *i1 = size - 1;
-      break;
+      for (ch = 0; ch < 4; ch++) {
+         float u = fabsf(s[ch]);
+         if (u >= 1.0F)
+            u = (float) size;
+         else
+            u *= size;
+         u -= 0.5F;
+         icoord0[ch] = util_ifloor(u);
+         icoord1[ch] = icoord0[ch] + 1;
+         if (icoord0[ch] < 0)
+            icoord0[ch] = 0;
+         if (icoord1[ch] >= (int) size)
+            icoord1[ch] = size - 1;
+         w[ch] = FRAC(u);
+      }
+      break;;
    case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER:
       {
          const float min = -1.0F / (2.0F * size);
          const float max = 1.0F - min;
-         u = FABSF(s);
-         if (u <= min)
-            u = min * size;
-         else if (u >= max)
-            u = max * size;
-         else
-            u *= size;
-         u -= 0.5F;
-         *i0 = ifloor(u);
-         *i1 = *i0 + 1;
+         for (ch = 0; ch < 4; ch++) {
+            float u = fabsf(s[ch]);
+            if (u <= min)
+               u = min * size;
+            else if (u >= max)
+               u = max * size;
+            else
+               u *= size;
+            u -= 0.5F;
+            icoord0[ch] = util_ifloor(u);
+            icoord1[ch] = icoord0[ch] + 1;
+            w[ch] = FRAC(u);
+         }
       }
-      break;
+      break;;
    default:
       assert(0);
    }
-   *a = FRAC(u);
 }
 
 
@@ -336,21 +383,27 @@ linear_texcoord(unsigned wrapMode, float s, unsigned size,
  * For RECT textures / unnormalized texcoords
  * Only a subset of wrap modes supported.
  */
-static INLINE int
-nearest_texcoord_unnorm(unsigned wrapMode, float s, unsigned size)
+static INLINE void
+nearest_texcoord_unnorm_4(unsigned wrapMode, const float s[4], unsigned size,
+                          int icoord[4])
 {
-   int i;
+   uint ch;
    switch (wrapMode) {
    case PIPE_TEX_WRAP_CLAMP:
-      i = ifloor(s);
-      return CLAMP(i, 0, (int) size-1);
+      for (ch = 0; ch < 4; ch++) {
+         int i = util_ifloor(s[ch]);
+         icoord[ch]= CLAMP(i, 0, (int) size-1);
+      }
+      return;
    case PIPE_TEX_WRAP_CLAMP_TO_EDGE:
       /* fall-through */
    case PIPE_TEX_WRAP_CLAMP_TO_BORDER:
-      return ifloor( CLAMP(s, 0.5F, (float) size - 0.5F) );
+      for (ch = 0; ch < 4; ch++) {
+         icoord[ch]= util_ifloor( CLAMP(s[ch], 0.5F, (float) size - 0.5F) );
+      }
+      return;
    default:
       assert(0);
-      return 0;
    }
 }
 
@@ -360,30 +413,36 @@ nearest_texcoord_unnorm(unsigned wrapMode, float s, unsigned size)
  * Only a subset of wrap modes supported.
  */
 static INLINE void
-linear_texcoord_unnorm(unsigned wrapMode, float s, unsigned size,
-                       int *i0, int *i1, float *a)
+linear_texcoord_unnorm_4(unsigned wrapMode, const float s[4], unsigned size,
+                         int icoord0[4], int icoord1[4], float w[4])
 {
+   uint ch;
    switch (wrapMode) {
    case PIPE_TEX_WRAP_CLAMP:
-      /* Not exactly what the spec says, but it matches NVIDIA output */
-      s = CLAMP(s - 0.5F, 0.0f, (float) size - 1.0f);
-      *i0 = ifloor(s);
-      *i1 = *i0 + 1;
-      break;
+      for (ch = 0; ch < 4; ch++) {
+         /* Not exactly what the spec says, but it matches NVIDIA output */
+         float u = CLAMP(s[ch] - 0.5F, 0.0f, (float) size - 1.0f);
+         icoord0[ch] = util_ifloor(u);
+         icoord1[ch] = icoord0[ch] + 1;
+         w[ch] = FRAC(u);
+      }
+      return;
    case PIPE_TEX_WRAP_CLAMP_TO_EDGE:
       /* fall-through */
    case PIPE_TEX_WRAP_CLAMP_TO_BORDER:
-      s = CLAMP(s, 0.5F, (float) size - 0.5F);
-      s -= 0.5F;
-      *i0 = ifloor(s);
-      *i1 = *i0 + 1;
-      if (*i1 > (int) size - 1)
-         *i1 = size - 1;
+      for (ch = 0; ch < 4; ch++) {
+         float u = CLAMP(s[ch], 0.5F, (float) size - 0.5F);
+         u -= 0.5F;
+         icoord0[ch] = util_ifloor(u);
+         icoord1[ch] = icoord0[ch] + 1;
+         if (icoord1[ch] > (int) size - 1)
+            icoord1[ch] = size - 1;
+         w[ch] = FRAC(u);
+      }
       break;
    default:
       assert(0);
    }
-   *a = FRAC(s);
 }
 
 
@@ -401,7 +460,7 @@ choose_cube_face(float rx, float ry, float rz, float *newS, float *newT)
        +rz          TEXTURE_CUBE_MAP_POSITIVE_Z_EXT    +rx    -ry   rz
        -rz          TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT    -rx    -ry   rz
    */
-   const float arx = FABSF(rx), ary = FABSF(ry), arz = FABSF(rz);
+   const float arx = fabsf(rx), ary = fabsf(ry), arz = fabsf(rz);
    unsigned face;
    float sc, tc, ma;
 
@@ -462,7 +521,8 @@ choose_cube_face(float rx, float ry, float rz, float *newS, float *newT)
  * This is only done for fragment shaders, not vertex shaders.
  */
 static float
-compute_lambda(struct tgsi_sampler *sampler,
+compute_lambda(const struct pipe_texture *tex,
+               const struct pipe_sampler_state *sampler,
                const float s[QUAD_SIZE],
                const float t[QUAD_SIZE],
                const float p[QUAD_SIZE],
@@ -470,38 +530,38 @@ compute_lambda(struct tgsi_sampler *sampler,
 {
    float rho, lambda;
 
-   assert(sampler->state->normalized_coords);
+   assert(sampler->normalized_coords);
 
    assert(s);
    {
       float dsdx = s[QUAD_BOTTOM_RIGHT] - s[QUAD_BOTTOM_LEFT];
       float dsdy = s[QUAD_TOP_LEFT]     - s[QUAD_BOTTOM_LEFT];
-      dsdx = FABSF(dsdx);
-      dsdy = FABSF(dsdy);
-      rho = MAX2(dsdx, dsdy) * sampler->texture->width[0];
+      dsdx = fabsf(dsdx);
+      dsdy = fabsf(dsdy);
+      rho = MAX2(dsdx, dsdy) * tex->width[0];
    }
    if (t) {
       float dtdx = t[QUAD_BOTTOM_RIGHT] - t[QUAD_BOTTOM_LEFT];
       float dtdy = t[QUAD_TOP_LEFT]     - t[QUAD_BOTTOM_LEFT];
       float max;
-      dtdx = FABSF(dtdx);
-      dtdy = FABSF(dtdy);
-      max = MAX2(dtdx, dtdy) * sampler->texture->height[0];
+      dtdx = fabsf(dtdx);
+      dtdy = fabsf(dtdy);
+      max = MAX2(dtdx, dtdy) * tex->height[0];
       rho = MAX2(rho, max);
    }
    if (p) {
       float dpdx = p[QUAD_BOTTOM_RIGHT] - p[QUAD_BOTTOM_LEFT];
       float dpdy = p[QUAD_TOP_LEFT]     - p[QUAD_BOTTOM_LEFT];
       float max;
-      dpdx = FABSF(dpdx);
-      dpdy = FABSF(dpdy);
-      max = MAX2(dpdx, dpdy) * sampler->texture->depth[0];
+      dpdx = fabsf(dpdx);
+      dpdy = fabsf(dpdy);
+      max = MAX2(dpdx, dpdy) * tex->depth[0];
       rho = MAX2(rho, max);
    }
 
-   lambda = LOG2(rho);
-   lambda += lodbias + sampler->state->lod_bias;
-   lambda = CLAMP(lambda, sampler->state->min_lod, sampler->state->max_lod);
+   lambda = util_fast_log2(rho);
+   lambda += lodbias + sampler->lod_bias;
+   lambda = CLAMP(lambda, sampler->min_lod, sampler->max_lod);
 
    return lambda;
 }
@@ -513,68 +573,74 @@ compute_lambda(struct tgsi_sampler *sampler,
  * 2. Determine if we're minifying or magnifying
  * 3. If minifying, choose mipmap levels
  * 4. Return image filter to use within mipmap images
+ * \param level0  Returns first mipmap level to sample from
+ * \param level1  Returns second mipmap level to sample from
+ * \param levelBlend  Returns blend factor between levels, in [0,1]
+ * \param imgFilter  Returns either the min or mag filter, depending on lambda
  */
 static void
-choose_mipmap_levels(struct tgsi_sampler *sampler,
+choose_mipmap_levels(const struct pipe_texture *texture,
+                     const struct pipe_sampler_state *sampler,
                      const float s[QUAD_SIZE],
                      const float t[QUAD_SIZE],
                      const float p[QUAD_SIZE],
+                     boolean computeLambda,
                      float lodbias,
                      unsigned *level0, unsigned *level1, float *levelBlend,
                      unsigned *imgFilter)
 {
-   if (sampler->state->min_mip_filter == PIPE_TEX_MIPFILTER_NONE) {
+   if (sampler->min_mip_filter == PIPE_TEX_MIPFILTER_NONE) {
       /* no mipmap selection needed */
-      *level0 = *level1 = CLAMP((int) sampler->state->min_lod,
-                                0, (int) sampler->texture->last_level);
+      *level0 = *level1 = CLAMP((int) sampler->min_lod,
+                                0, (int) texture->last_level);
 
-      if (sampler->state->min_img_filter != sampler->state->mag_img_filter) {
+      if (sampler->min_img_filter != sampler->mag_img_filter) {
          /* non-mipmapped texture, but still need to determine if doing
           * minification or magnification.
           */
-         float lambda = compute_lambda(sampler, s, t, p, lodbias);
+         float lambda = compute_lambda(texture, sampler, s, t, p, lodbias);
          if (lambda <= 0.0) {
-            *imgFilter = sampler->state->mag_img_filter;
+            *imgFilter = sampler->mag_img_filter;
          }
          else {
-            *imgFilter = sampler->state->min_img_filter;
+            *imgFilter = sampler->min_img_filter;
          }
       }
       else {
-         *imgFilter = sampler->state->mag_img_filter;
+         *imgFilter = sampler->mag_img_filter;
       }
    }
    else {
       float lambda;
 
-      if (1)
+      if (computeLambda)
          /* fragment shader */
-         lambda = compute_lambda(sampler, s, t, p, lodbias);
+         lambda = compute_lambda(texture, sampler, s, t, p, lodbias);
       else
          /* vertex shader */
          lambda = lodbias; /* not really a bias, but absolute LOD */
 
       if (lambda <= 0.0) { /* XXX threshold depends on the filter */
          /* magnifying */
-         *imgFilter = sampler->state->mag_img_filter;
+         *imgFilter = sampler->mag_img_filter;
          *level0 = *level1 = 0;
       }
       else {
          /* minifying */
-         *imgFilter = sampler->state->min_img_filter;
+         *imgFilter = sampler->min_img_filter;
 
          /* choose mipmap level(s) and compute the blend factor between them */
-         if (sampler->state->min_mip_filter == PIPE_TEX_MIPFILTER_NEAREST) {
+         if (sampler->min_mip_filter == PIPE_TEX_MIPFILTER_NEAREST) {
             /* Nearest mipmap level */
             const int lvl = (int) (lambda + 0.5);
             *level0 =
-            *level1 = CLAMP(lvl, 0, (int) sampler->texture->last_level);
+            *level1 = CLAMP(lvl, 0, (int) texture->last_level);
          }
          else {
             /* Linear interpolation between mipmap levels */
             const int lvl = (int) lambda;
-            *level0 = CLAMP(lvl,     0, (int) sampler->texture->last_level);
-            *level1 = CLAMP(lvl + 1, 0, (int) sampler->texture->last_level);
+            *level0 = CLAMP(lvl,     0, (int) texture->last_level);
+            *level1 = CLAMP(lvl + 1, 0, (int) texture->last_level);
             *levelBlend = FRAC(lambda);  /* blending weight between levels */
          }
       }
@@ -597,28 +663,40 @@ choose_mipmap_levels(struct tgsi_sampler *sampler,
  * sp_get_cached_tile_tex() function.  Also, get 4 texels instead of 1...
  */
 static void
-get_texel(struct tgsi_sampler *sampler,
+get_texel(const struct tgsi_sampler *tgsi_sampler,
           unsigned face, unsigned level, int x, int y, int z,
           float rgba[NUM_CHANNELS][QUAD_SIZE], unsigned j)
 {
-   if (x < 0 || x >= sampler->texture->width[level] ||
-       y < 0 || y >= sampler->texture->height[level] ||
-       z < 0 || z >= sampler->texture->depth[level]) {
-      rgba[0][j] = sampler->state->border_color[0];
-      rgba[1][j] = sampler->state->border_color[1];
-      rgba[2][j] = sampler->state->border_color[2];
-      rgba[3][j] = sampler->state->border_color[3];
+   const struct sp_shader_sampler *samp = sp_shader_sampler(tgsi_sampler);
+   const struct pipe_texture *texture = samp->texture;
+   const struct pipe_sampler_state *sampler = samp->sampler;
+
+   if (x < 0 || x >= (int) texture->width[level] ||
+       y < 0 || y >= (int) texture->height[level] ||
+       z < 0 || z >= (int) texture->depth[level]) {
+      rgba[0][j] = sampler->border_color[0];
+      rgba[1][j] = sampler->border_color[1];
+      rgba[2][j] = sampler->border_color[2];
+      rgba[3][j] = sampler->border_color[3];
    }
    else {
-      const int tx = x % TILE_SIZE;
-      const int ty = y % TILE_SIZE;
-      const struct softpipe_cached_tile *tile
-         = sp_get_cached_tile_tex(sampler->pipe, sampler->cache,
-                                  x, y, z, face, level);
+      const unsigned tx = x % TILE_SIZE;
+      const unsigned ty = y % TILE_SIZE;
+      const struct softpipe_cached_tile *tile;
+
+      tile = sp_get_cached_tile_tex(samp->cache, 
+                                    tile_address(x, y, z, face, level));
+
       rgba[0][j] = tile->data.color[ty][tx][0];
       rgba[1][j] = tile->data.color[ty][tx][1];
       rgba[2][j] = tile->data.color[ty][tx][2];
       rgba[3][j] = tile->data.color[ty][tx][3];
+      if (0)
+      {
+         debug_printf("Get texel %f %f %f %f from %s\n",
+                      rgba[0][j], rgba[1][j], rgba[2][j], rgba[3][j],
+                      pf_name(texture->format));
+      }
    }
 }
 
@@ -627,15 +705,18 @@ get_texel(struct tgsi_sampler *sampler,
  * Compare texcoord 'p' (aka R) against texture value 'rgba[0]'
  * When we sampled the depth texture, the depth value was put into all
  * RGBA channels.  We look at the red channel here.
+ * \param rgba  quad of (depth) texel values
+ * \param p  texture 'P' components for four pixels in quad
+ * \param j  which pixel in the quad to test [0..3]
  */
 static INLINE void
-shadow_compare(uint compare_func,
+shadow_compare(const struct pipe_sampler_state *sampler,
                float rgba[NUM_CHANNELS][QUAD_SIZE],
                const float p[QUAD_SIZE],
                uint j)
 {
    int k;
-   switch (compare_func) {
+   switch (sampler->compare_func) {
    case PIPE_FUNC_LESS:
       k = p[j] < rgba[0][j];
       break;
@@ -666,7 +747,81 @@ shadow_compare(uint compare_func,
       break;
    }
 
+   /* XXX returning result for default GL_DEPTH_TEXTURE_MODE = GL_LUMINANCE */
    rgba[0][j] = rgba[1][j] = rgba[2][j] = (float) k;
+   rgba[3][j] = 1.0F;
+}
+
+
+/**
+ * As above, but do four z/texture comparisons.
+ */
+static INLINE void
+shadow_compare4(const struct pipe_sampler_state *sampler,
+                float rgba[NUM_CHANNELS][QUAD_SIZE],
+                const float p[QUAD_SIZE])
+{
+   int j, k0, k1, k2, k3;
+   float val;
+
+   /* compare four texcoords vs. four texture samples */
+   switch (sampler->compare_func) {
+   case PIPE_FUNC_LESS:
+      k0 = p[0] < rgba[0][0];
+      k1 = p[1] < rgba[0][1];
+      k2 = p[2] < rgba[0][2];
+      k3 = p[3] < rgba[0][3];
+      break;
+   case PIPE_FUNC_LEQUAL:
+      k0 = p[0] <= rgba[0][0];
+      k1 = p[1] <= rgba[0][1];
+      k2 = p[2] <= rgba[0][2];
+      k3 = p[3] <= rgba[0][3];
+      break;
+   case PIPE_FUNC_GREATER:
+      k0 = p[0] > rgba[0][0];
+      k1 = p[1] > rgba[0][1];
+      k2 = p[2] > rgba[0][2];
+      k3 = p[3] > rgba[0][3];
+      break;
+   case PIPE_FUNC_GEQUAL:
+      k0 = p[0] >= rgba[0][0];
+      k1 = p[1] >= rgba[0][1];
+      k2 = p[2] >= rgba[0][2];
+      k3 = p[3] >= rgba[0][3];
+      break;
+   case PIPE_FUNC_EQUAL:
+      k0 = p[0] == rgba[0][0];
+      k1 = p[1] == rgba[0][1];
+      k2 = p[2] == rgba[0][2];
+      k3 = p[3] == rgba[0][3];
+      break;
+   case PIPE_FUNC_NOTEQUAL:
+      k0 = p[0] != rgba[0][0];
+      k1 = p[1] != rgba[0][1];
+      k2 = p[2] != rgba[0][2];
+      k3 = p[3] != rgba[0][3];
+      break;
+   case PIPE_FUNC_ALWAYS:
+      k0 = k1 = k2 = k3 = 1;
+      break;
+   case PIPE_FUNC_NEVER:
+      k0 = k1 = k2 = k3 = 0;
+      break;
+   default:
+      k0 = k1 = k2 = k3 = 0;
+      assert(0);
+      break;
+   }
+
+   /* convert four pass/fail values to an intensity in [0,1] */
+   val = 0.25F * (k0 + k1 + k2 + k3);
+
+   /* XXX returning result for default GL_DEPTH_TEXTURE_MODE = GL_LUMINANCE */
+   for (j = 0; j < 4; j++) {
+      rgba[0][j] = rgba[1][j] = rgba[2][j] = val;
+      rgba[3][j] = 1.0F;
+   }
 }
 
 
@@ -675,103 +830,115 @@ shadow_compare(uint compare_func,
  * Could probably extend for 3D...
  */
 static void
-sp_get_samples_2d_common(struct tgsi_sampler *sampler,
+sp_get_samples_2d_common(const struct tgsi_sampler *tgsi_sampler,
                          const float s[QUAD_SIZE],
                          const float t[QUAD_SIZE],
                          const float p[QUAD_SIZE],
+                         boolean computeLambda,
                          float lodbias,
                          float rgba[NUM_CHANNELS][QUAD_SIZE],
                          const unsigned faces[4])
 {
-   const uint compare_func = sampler->state->compare_func;
+   const struct sp_shader_sampler *samp = sp_shader_sampler(tgsi_sampler);
+   const struct pipe_texture *texture = samp->texture;
+   const struct pipe_sampler_state *sampler = samp->sampler;
    unsigned level0, level1, j, imgFilter;
    int width, height;
    float levelBlend;
 
-   choose_mipmap_levels(sampler, s, t, p, lodbias,
+   choose_mipmap_levels(texture, sampler, s, t, p, computeLambda, lodbias,
                         &level0, &level1, &levelBlend, &imgFilter);
 
-   assert(sampler->state->normalized_coords);
+   assert(sampler->normalized_coords);
 
-   width = sampler->texture->width[level0];
-   height = sampler->texture->height[level0];
+   width = texture->width[level0];
+   height = texture->height[level0];
 
    assert(width > 0);
 
    switch (imgFilter) {
    case PIPE_TEX_FILTER_NEAREST:
-      for (j = 0; j < QUAD_SIZE; j++) {
-         int x = nearest_texcoord(sampler->state->wrap_s, s[j], width);
-         int y = nearest_texcoord(sampler->state->wrap_t, t[j], height);
-         get_texel(sampler, faces[j], level0, x, y, 0, rgba, j);
-         if (sampler->state->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE) {
-            shadow_compare(compare_func, rgba, p, j);
-         }
-
-         if (level0 != level1) {
-            /* get texels from second mipmap level and blend */
-            float rgba2[4][4];
-            unsigned c;
-            x = x / 2;
-            y = y / 2;
-            get_texel(sampler, faces[j], level1, x, y, 0, rgba2, j);
-            if (sampler->state->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE){
-               shadow_compare(compare_func, rgba2, p, j);
+      {
+         int x[4], y[4];
+         nearest_texcoord_4(sampler->wrap_s, s, width, x);
+         nearest_texcoord_4(sampler->wrap_t, t, height, y);
+
+         for (j = 0; j < QUAD_SIZE; j++) {
+            get_texel(tgsi_sampler, faces[j], level0, x[j], y[j], 0, rgba, j);
+            if (sampler->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE) {
+               shadow_compare(sampler, rgba, p, j);
             }
 
-            for (c = 0; c < NUM_CHANNELS; c++) {
-               rgba[c][j] = LERP(levelBlend, rgba[c][j], rgba2[c][j]);
+            if (level0 != level1) {
+               /* get texels from second mipmap level and blend */
+               float rgba2[4][4];
+               unsigned c;
+               x[j] /= 2;
+               y[j] /= 2;
+               get_texel(tgsi_sampler, faces[j], level1, x[j], y[j], 0,
+                         rgba2, j);
+               if (sampler->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE){
+                  shadow_compare(sampler, rgba2, p, j);
+               }
+
+               for (c = 0; c < NUM_CHANNELS; c++) {
+                  rgba[c][j] = lerp(levelBlend, rgba[c][j], rgba2[c][j]);
+               }
             }
          }
       }
       break;
    case PIPE_TEX_FILTER_LINEAR:
    case PIPE_TEX_FILTER_ANISO:
-      for (j = 0; j < QUAD_SIZE; j++) {
-         float tx[4][4], a, b;
-         int x0, y0, x1, y1, c;
-         linear_texcoord(sampler->state->wrap_s, s[j], width,  &x0, &x1, &a);
-         linear_texcoord(sampler->state->wrap_t, t[j], height, &y0, &y1, &b);
-         get_texel(sampler, faces[j], level0, x0, y0, 0, tx, 0);
-         get_texel(sampler, faces[j], level0, x1, y0, 0, tx, 1);
-         get_texel(sampler, faces[j], level0, x0, y1, 0, tx, 2);
-         get_texel(sampler, faces[j], level0, x1, y1, 0, tx, 3);
-         if (sampler->state->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE) {
-            shadow_compare(compare_func, tx, p, 0);
-            shadow_compare(compare_func, tx, p, 1);
-            shadow_compare(compare_func, tx, p, 2);
-            shadow_compare(compare_func, tx, p, 3);
-         }
-
-         for (c = 0; c < 4; c++) {
-            rgba[c][j] = lerp_2d(a, b, tx[c][0], tx[c][1], tx[c][2], tx[c][3]);
-         }
-
-         if (level0 != level1) {
-            /* get texels from second mipmap level and blend */
-            float rgba2[4][4];
-            x0 = x0 / 2;
-            y0 = y0 / 2;
-            x1 = x1 / 2;
-            y1 = y1 / 2;
-            get_texel(sampler, faces[j], level1, x0, y0, 0, tx, 0);
-            get_texel(sampler, faces[j], level1, x1, y0, 0, tx, 1);
-            get_texel(sampler, faces[j], level1, x0, y1, 0, tx, 2);
-            get_texel(sampler, faces[j], level1, x1, y1, 0, tx, 3);
-            if (sampler->state->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE){
-               shadow_compare(compare_func, tx, p, 0);
-               shadow_compare(compare_func, tx, p, 1);
-               shadow_compare(compare_func, tx, p, 2);
-               shadow_compare(compare_func, tx, p, 3);
+      {
+         int x0[4], y0[4], x1[4], y1[4];
+         float xw[4], yw[4]; /* weights */
+
+         linear_texcoord_4(sampler->wrap_s, s, width, x0, x1, xw);
+         linear_texcoord_4(sampler->wrap_t, t, height, y0, y1, yw);
+
+         for (j = 0; j < QUAD_SIZE; j++) {
+            float tx[4][4]; /* texels */
+            int c;
+            get_texel(tgsi_sampler, faces[j], level0, x0[j], y0[j], 0, tx, 0);
+            get_texel(tgsi_sampler, faces[j], level0, x1[j], y0[j], 0, tx, 1);
+            get_texel(tgsi_sampler, faces[j], level0, x0[j], y1[j], 0, tx, 2);
+            get_texel(tgsi_sampler, faces[j], level0, x1[j], y1[j], 0, tx, 3);
+            if (sampler->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE) {
+               shadow_compare4(sampler, tx, p);
             }
 
+            /* interpolate R, G, B, A */
             for (c = 0; c < 4; c++) {
-               rgba2[c][j] = lerp_2d(a, b,
-                                     tx[c][0], tx[c][1], tx[c][2], tx[c][3]);
+               rgba[c][j] = lerp_2d(xw[j], yw[j],
+                                    tx[c][0], tx[c][1],
+                                    tx[c][2], tx[c][3]);
             }
 
-            for (c = 0; c < NUM_CHANNELS; c++) {
-               rgba[c][j] = LERP(levelBlend, rgba[c][j], rgba2[c][j]);
+            if (level0 != level1) {
+               /* get texels from second mipmap level and blend */
+               float rgba2[4][4];
+               x0[j] /= 2;
+               y0[j] /= 2;
+               x1[j] /= 2;
+               y1[j] /= 2;
+               get_texel(tgsi_sampler, faces[j], level1, x0[j], y0[j], 0, tx, 0);
+               get_texel(tgsi_sampler, faces[j], level1, x1[j], y0[j], 0, tx, 1);
+               get_texel(tgsi_sampler, faces[j], level1, x0[j], y1[j], 0, tx, 2);
+               get_texel(tgsi_sampler, faces[j], level1, x1[j], y1[j], 0, tx, 3);
+               if (sampler->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE){
+                  shadow_compare4(sampler, tx, p);
+               }
+
+               /* interpolate R, G, B, A */
+               for (c = 0; c < 4; c++) {
+                  rgba2[c][j] = lerp_2d(xw[j], yw[j],
+                                        tx[c][0], tx[c][1], tx[c][2], tx[c][3]);
+               }
+
+               for (c = 0; c < NUM_CHANNELS; c++) {
+                  rgba[c][j] = lerp(levelBlend, rgba[c][j], rgba2[c][j]);
+               }
             }
          }
       }
@@ -782,55 +949,63 @@ sp_get_samples_2d_common(struct tgsi_sampler *sampler,
 }
 
 
-static void
-sp_get_samples_1d(struct tgsi_sampler *sampler,
+static INLINE void
+sp_get_samples_1d(const struct tgsi_sampler *sampler,
                   const float s[QUAD_SIZE],
                   const float t[QUAD_SIZE],
                   const float p[QUAD_SIZE],
+                  boolean computeLambda,
                   float lodbias,
                   float rgba[NUM_CHANNELS][QUAD_SIZE])
 {
    static const unsigned faces[4] = {0, 0, 0, 0};
    static const float tzero[4] = {0, 0, 0, 0};
-   sp_get_samples_2d_common(sampler, s, tzero, NULL, lodbias, rgba, faces);
+   sp_get_samples_2d_common(sampler, s, tzero, NULL,
+                            computeLambda, lodbias, rgba, faces);
 }
 
 
-static void
-sp_get_samples_2d(struct tgsi_sampler *sampler,
+static INLINE void
+sp_get_samples_2d(const struct tgsi_sampler *sampler,
                   const float s[QUAD_SIZE],
                   const float t[QUAD_SIZE],
                   const float p[QUAD_SIZE],
+                  boolean computeLambda,
                   float lodbias,
                   float rgba[NUM_CHANNELS][QUAD_SIZE])
 {
    static const unsigned faces[4] = {0, 0, 0, 0};
-   sp_get_samples_2d_common(sampler, s, t, p, lodbias, rgba, faces);
+   sp_get_samples_2d_common(sampler, s, t, p,
+                            computeLambda, lodbias, rgba, faces);
 }
 
 
-static void
-sp_get_samples_3d(struct tgsi_sampler *sampler,
+static INLINE void
+sp_get_samples_3d(const struct tgsi_sampler *tgsi_sampler,
                   const float s[QUAD_SIZE],
                   const float t[QUAD_SIZE],
                   const float p[QUAD_SIZE],
+                  boolean computeLambda,
                   float lodbias,
                   float rgba[NUM_CHANNELS][QUAD_SIZE])
 {
+   const struct sp_shader_sampler *samp = sp_shader_sampler(tgsi_sampler);
+   const struct pipe_texture *texture = samp->texture;
+   const struct pipe_sampler_state *sampler = samp->sampler;
    /* get/map pipe_surfaces corresponding to 3D tex slices */
    unsigned level0, level1, j, imgFilter;
    int width, height, depth;
    float levelBlend;
    const uint face = 0;
 
-   choose_mipmap_levels(sampler, s, t, p, lodbias,
+   choose_mipmap_levels(texture, sampler, s, t, p, computeLambda, lodbias,
                         &level0, &level1, &levelBlend, &imgFilter);
 
-   assert(sampler->state->normalized_coords);
+   assert(sampler->normalized_coords);
 
-   width = sampler->texture->width[level0];
-   height = sampler->texture->height[level0];
-   depth = sampler->texture->depth[level0];
+   width = texture->width[level0];
+   height = texture->height[level0];
+   depth = texture->depth[level0];
 
    assert(width > 0);
    assert(height > 0);
@@ -838,89 +1013,89 @@ sp_get_samples_3d(struct tgsi_sampler *sampler,
 
    switch (imgFilter) {
    case PIPE_TEX_FILTER_NEAREST:
-      for (j = 0; j < QUAD_SIZE; j++) {
-         int x = nearest_texcoord(sampler->state->wrap_s, s[j], width);
-         int y = nearest_texcoord(sampler->state->wrap_t, t[j], height);
-         int z = nearest_texcoord(sampler->state->wrap_r, p[j], depth);
-         get_texel(sampler, face, level0, x, y, z, rgba, j);
-
-         if (level0 != level1) {
-            /* get texels from second mipmap level and blend */
-            float rgba2[4][4];
-            unsigned c;
-            x /= 2;
-            y /= 2;
-            z /= 2;
-            get_texel(sampler, face, level1, x, y, z, rgba2, j);
-            for (c = 0; c < NUM_CHANNELS; c++) {
-               rgba[c][j] = LERP(levelBlend, rgba2[c][j], rgba[c][j]);
+      {
+         int x[4], y[4], z[4];
+         nearest_texcoord_4(sampler->wrap_s, s, width, x);
+         nearest_texcoord_4(sampler->wrap_t, t, height, y);
+         nearest_texcoord_4(sampler->wrap_r, p, depth, z);
+         for (j = 0; j < QUAD_SIZE; j++) {
+            get_texel(tgsi_sampler, face, level0, x[j], y[j], z[j], rgba, j);
+            if (level0 != level1) {
+               /* get texels from second mipmap level and blend */
+               float rgba2[4][4];
+               unsigned c;
+               x[j] /= 2;
+               y[j] /= 2;
+               z[j] /= 2;
+               get_texel(tgsi_sampler, face, level1, x[j], y[j], z[j], rgba2, j);
+               for (c = 0; c < NUM_CHANNELS; c++) {
+                  rgba[c][j] = lerp(levelBlend, rgba2[c][j], rgba[c][j]);
+               }
             }
          }
       }
       break;
    case PIPE_TEX_FILTER_LINEAR:
    case PIPE_TEX_FILTER_ANISO:
-      for (j = 0; j < QUAD_SIZE; j++) {
-         float texel0[4][4], texel1[4][4];
-         float xw, yw, zw; /* interpolation weights */
-         int x0, x1, y0, y1, z0, z1, c;
-         linear_texcoord(sampler->state->wrap_s, s[j], width,  &x0, &x1, &xw);
-         linear_texcoord(sampler->state->wrap_t, t[j], height, &y0, &y1, &yw);
-         linear_texcoord(sampler->state->wrap_r, p[j], depth,  &z0, &z1, &zw);
-         get_texel(sampler, face, level0, x0, y0, z0, texel0, 0);
-         get_texel(sampler, face, level0, x1, y0, z0, texel0, 1);
-         get_texel(sampler, face, level0, x0, y1, z0, texel0, 2);
-         get_texel(sampler, face, level0, x1, y1, z0, texel0, 3);
-         get_texel(sampler, face, level0, x0, y0, z1, texel1, 0);
-         get_texel(sampler, face, level0, x1, y0, z1, texel1, 1);
-         get_texel(sampler, face, level0, x0, y1, z1, texel1, 2);
-         get_texel(sampler, face, level0, x1, y1, z1, texel1, 3);
-
-         /* 3D lerp */
-         for (c = 0; c < 4; c++) {
-            float ctemp0[4][4], ctemp1[4][4];
-            ctemp0[c][j] = lerp_2d(xw, yw,
-                                   texel0[c][0], texel0[c][1],
-                                   texel0[c][2], texel0[c][3]);
-            ctemp1[c][j] = lerp_2d(xw, yw,
-                                   texel1[c][0], texel1[c][1],
-                                   texel1[c][2], texel1[c][3]);
-            rgba[c][j] = LERP(zw, ctemp0[c][j], ctemp1[c][j]);
-         }
-
-         if (level0 != level1) {
-            /* get texels from second mipmap level and blend */
-            float rgba2[4][4];
-            x0 /= 2;
-            y0 /= 2;
-            z0 /= 2;
-            x1 /= 2;
-            y1 /= 2;
-            z1 /= 2;
-            get_texel(sampler, face, level1, x0, y0, z0, texel0, 0);
-            get_texel(sampler, face, level1, x1, y0, z0, texel0, 1);
-            get_texel(sampler, face, level1, x0, y1, z0, texel0, 2);
-            get_texel(sampler, face, level1, x1, y1, z0, texel0, 3);
-            get_texel(sampler, face, level1, x0, y0, z1, texel1, 0);
-            get_texel(sampler, face, level1, x1, y0, z1, texel1, 1);
-            get_texel(sampler, face, level1, x0, y1, z1, texel1, 2);
-            get_texel(sampler, face, level1, x1, y1, z1, texel1, 3);
-
-            /* 3D lerp */
+      {
+         int x0[4], x1[4], y0[4], y1[4], z0[4], z1[4];
+         float xw[4], yw[4], zw[4]; /* interpolation weights */
+         linear_texcoord_4(sampler->wrap_s, s, width,  x0, x1, xw);
+         linear_texcoord_4(sampler->wrap_t, t, height, y0, y1, yw);
+         linear_texcoord_4(sampler->wrap_r, p, depth,  z0, z1, zw);
+
+         for (j = 0; j < QUAD_SIZE; j++) {
+            int c;
+            float tx0[4][4], tx1[4][4];
+            get_texel(tgsi_sampler, face, level0, x0[j], y0[j], z0[j], tx0, 0);
+            get_texel(tgsi_sampler, face, level0, x1[j], y0[j], z0[j], tx0, 1);
+            get_texel(tgsi_sampler, face, level0, x0[j], y1[j], z0[j], tx0, 2);
+            get_texel(tgsi_sampler, face, level0, x1[j], y1[j], z0[j], tx0, 3);
+            get_texel(tgsi_sampler, face, level0, x0[j], y0[j], z1[j], tx1, 0);
+            get_texel(tgsi_sampler, face, level0, x1[j], y0[j], z1[j], tx1, 1);
+            get_texel(tgsi_sampler, face, level0, x0[j], y1[j], z1[j], tx1, 2);
+            get_texel(tgsi_sampler, face, level0, x1[j], y1[j], z1[j], tx1, 3);
+
+            /* interpolate R, G, B, A */
             for (c = 0; c < 4; c++) {
-               float ctemp0[4][4], ctemp1[4][4];
-               ctemp0[c][j] = lerp_2d(xw, yw,
-                                      texel0[c][0], texel0[c][1],
-                                      texel0[c][2], texel0[c][3]);
-               ctemp1[c][j] = lerp_2d(xw, yw,
-                                      texel1[c][0], texel1[c][1],
-                                      texel1[c][2], texel1[c][3]);
-               rgba2[c][j] = LERP(zw, ctemp0[c][j], ctemp1[c][j]);
+               rgba[c][j] = lerp_3d(xw[j], yw[j], zw[j],
+                                    tx0[c][0], tx0[c][1],
+                                    tx0[c][2], tx0[c][3],
+                                    tx1[c][0], tx1[c][1],
+                                    tx1[c][2], tx1[c][3]);
             }
 
-            /* blend mipmap levels */
-            for (c = 0; c < NUM_CHANNELS; c++) {
-               rgba[c][j] = LERP(levelBlend, rgba[c][j], rgba2[c][j]);
+            if (level0 != level1) {
+               /* get texels from second mipmap level and blend */
+               float rgba2[4][4];
+               x0[j] /= 2;
+               y0[j] /= 2;
+               z0[j] /= 2;
+               x1[j] /= 2;
+               y1[j] /= 2;
+               z1[j] /= 2;
+               get_texel(tgsi_sampler, face, level1, x0[j], y0[j], z0[j], tx0, 0);
+               get_texel(tgsi_sampler, face, level1, x1[j], y0[j], z0[j], tx0, 1);
+               get_texel(tgsi_sampler, face, level1, x0[j], y1[j], z0[j], tx0, 2);
+               get_texel(tgsi_sampler, face, level1, x1[j], y1[j], z0[j], tx0, 3);
+               get_texel(tgsi_sampler, face, level1, x0[j], y0[j], z1[j], tx1, 0);
+               get_texel(tgsi_sampler, face, level1, x1[j], y0[j], z1[j], tx1, 1);
+               get_texel(tgsi_sampler, face, level1, x0[j], y1[j], z1[j], tx1, 2);
+               get_texel(tgsi_sampler, face, level1, x1[j], y1[j], z1[j], tx1, 3);
+
+               /* interpolate R, G, B, A */
+               for (c = 0; c < 4; c++) {
+                  rgba2[c][j] = lerp_3d(xw[j], yw[j], zw[j],
+                                        tx0[c][0], tx0[c][1],
+                                        tx0[c][2], tx0[c][3],
+                                        tx1[c][0], tx1[c][1],
+                                        tx1[c][2], tx1[c][3]);
+               }
+
+               /* blend mipmap levels */
+               for (c = 0; c < NUM_CHANNELS; c++) {
+                  rgba[c][j] = lerp(levelBlend, rgba[c][j], rgba2[c][j]);
+               }
             }
          }
       }
@@ -932,10 +1107,11 @@ sp_get_samples_3d(struct tgsi_sampler *sampler,
 
 
 static void
-sp_get_samples_cube(struct tgsi_sampler *sampler,
+sp_get_samples_cube(const struct tgsi_sampler *sampler,
                     const float s[QUAD_SIZE],
                     const float t[QUAD_SIZE],
                     const float p[QUAD_SIZE],
+                    boolean computeLambda,
                     float lodbias,
                     float rgba[NUM_CHANNELS][QUAD_SIZE])
 {
@@ -944,67 +1120,74 @@ sp_get_samples_cube(struct tgsi_sampler *sampler,
    for (j = 0; j < QUAD_SIZE; j++) {
       faces[j] = choose_cube_face(s[j], t[j], p[j], ssss + j, tttt + j);
    }
-   sp_get_samples_2d_common(sampler, ssss, tttt, NULL, lodbias, rgba, faces);
+   sp_get_samples_2d_common(sampler, ssss, tttt, NULL,
+                            computeLambda, lodbias, rgba, faces);
 }
 
 
 static void
-sp_get_samples_rect(struct tgsi_sampler *sampler,
+sp_get_samples_rect(const struct tgsi_sampler *tgsi_sampler,
                     const float s[QUAD_SIZE],
                     const float t[QUAD_SIZE],
                     const float p[QUAD_SIZE],
+                    boolean computeLambda,
                     float lodbias,
                     float rgba[NUM_CHANNELS][QUAD_SIZE])
 {
-   //sp_get_samples_2d_common(sampler, s, t, p, lodbias, rgba, faces);
-   static const uint face = 0;
-   const uint compare_func = sampler->state->compare_func;
+   const struct sp_shader_sampler *samp = sp_shader_sampler(tgsi_sampler);
+   const struct pipe_texture *texture = samp->texture;
+   const struct pipe_sampler_state *sampler = samp->sampler;
+   const uint face = 0;
    unsigned level0, level1, j, imgFilter;
    int width, height;
    float levelBlend;
 
-   choose_mipmap_levels(sampler, s, t, p, lodbias,
+   choose_mipmap_levels(texture, sampler, s, t, p, computeLambda, lodbias,
                         &level0, &level1, &levelBlend, &imgFilter);
 
    /* texture RECTS cannot be mipmapped */
    assert(level0 == level1);
 
-   width = sampler->texture->width[level0];
-   height = sampler->texture->height[level0];
+   width = texture->width[level0];
+   height = texture->height[level0];
 
    assert(width > 0);
 
    switch (imgFilter) {
    case PIPE_TEX_FILTER_NEAREST:
-      for (j = 0; j < QUAD_SIZE; j++) {
-         int x = nearest_texcoord_unnorm(sampler->state->wrap_s, s[j], width);
-         int y = nearest_texcoord_unnorm(sampler->state->wrap_t, t[j], height);
-         get_texel(sampler, face, level0, x, y, 0, rgba, j);
-         if (sampler->state->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE) {
-            shadow_compare(compare_func, rgba, p, j);
+      {
+         int x[4], y[4];
+         nearest_texcoord_unnorm_4(sampler->wrap_s, s, width, x);
+         nearest_texcoord_unnorm_4(sampler->wrap_t, t, height, y);
+         for (j = 0; j < QUAD_SIZE; j++) {
+            get_texel(tgsi_sampler, face, level0, x[j], y[j], 0, rgba, j);
+            if (sampler->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE) {
+               shadow_compare(sampler, rgba, p, j);
+            }
          }
       }
       break;
    case PIPE_TEX_FILTER_LINEAR:
    case PIPE_TEX_FILTER_ANISO:
-      for (j = 0; j < QUAD_SIZE; j++) {
-         float tx[4][4], a, b;
-         int x0, y0, x1, y1, c;
-         linear_texcoord_unnorm(sampler->state->wrap_s, s[j], width,  &x0, &x1, &a);
-         linear_texcoord_unnorm(sampler->state->wrap_t, t[j], height, &y0, &y1, &b);
-         get_texel(sampler, face, level0, x0, y0, 0, tx, 0);
-         get_texel(sampler, face, level0, x1, y0, 0, tx, 1);
-         get_texel(sampler, face, level0, x0, y1, 0, tx, 2);
-         get_texel(sampler, face, level0, x1, y1, 0, tx, 3);
-         if (sampler->state->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE) {
-            shadow_compare(compare_func, tx, p, 0);
-            shadow_compare(compare_func, tx, p, 1);
-            shadow_compare(compare_func, tx, p, 2);
-            shadow_compare(compare_func, tx, p, 3);
-         }
-
-         for (c = 0; c < 4; c++) {
-            rgba[c][j] = lerp_2d(a, b, tx[c][0], tx[c][1], tx[c][2], tx[c][3]);
+      {
+         int x0[4], y0[4], x1[4], y1[4];
+         float xw[4], yw[4]; /* weights */
+         linear_texcoord_unnorm_4(sampler->wrap_s, s, width,  x0, x1, xw);
+         linear_texcoord_unnorm_4(sampler->wrap_t, t, height, y0, y1, yw);
+         for (j = 0; j < QUAD_SIZE; j++) {
+            float tx[4][4]; /* texels */
+            int c;
+            get_texel(tgsi_sampler, face, level0, x0[j], y0[j], 0, tx, 0);
+            get_texel(tgsi_sampler, face, level0, x1[j], y0[j], 0, tx, 1);
+            get_texel(tgsi_sampler, face, level0, x0[j], y1[j], 0, tx, 2);
+            get_texel(tgsi_sampler, face, level0, x1[j], y1[j], 0, tx, 3);
+            if (sampler->compare_mode == PIPE_TEX_COMPARE_R_TO_TEXTURE) {
+               shadow_compare4(sampler, tx, p);
+            }
+            for (c = 0; c < 4; c++) {
+               rgba[c][j] = lerp_2d(xw[j], yw[j],
+                                    tx[c][0], tx[c][1], tx[c][2], tx[c][3]);
+            }
          }
       }
       break;
@@ -1014,49 +1197,43 @@ sp_get_samples_rect(struct tgsi_sampler *sampler,
 }
 
 
-
-
 /**
- * Called via tgsi_sampler::get_samples()
- * Use the sampler's state setting to get a filtered RGBA value
- * from the sampler's texture.
- *
- * XXX we can implement many versions of this function, each
- * tightly coded for a specific combination of sampler state
- * (nearest + repeat), (bilinear mipmap + clamp), etc.
- *
- * The update_samplers() function in st_atom_sampler.c could create
- * a new tgsi_sampler object for each state combo it finds....
+ * Common code for vertex/fragment program texture sampling.
  */
-void
-sp_get_samples(struct tgsi_sampler *sampler,
+static INLINE void
+sp_get_samples(struct tgsi_sampler *tgsi_sampler,
                const float s[QUAD_SIZE],
                const float t[QUAD_SIZE],
                const float p[QUAD_SIZE],
+               boolean computeLambda,
                float lodbias,
                float rgba[NUM_CHANNELS][QUAD_SIZE])
 {
-   if (!sampler->texture)
+   const struct sp_shader_sampler *samp = sp_shader_sampler(tgsi_sampler);
+   const struct pipe_texture *texture = samp->texture;
+   const struct pipe_sampler_state *sampler = samp->sampler;
+
+   if (!texture)
       return;
 
-   switch (sampler->texture->target) {
+   switch (texture->target) {
    case PIPE_TEXTURE_1D:
-      assert(sampler->state->normalized_coords);
-      sp_get_samples_1d(sampler, s, t, p, lodbias, rgba);
+      assert(sampler->normalized_coords);
+      sp_get_samples_1d(tgsi_sampler, s, t, p, computeLambda, lodbias, rgba);
       break;
    case PIPE_TEXTURE_2D:
-      if (sampler->state->normalized_coords)
-         sp_get_samples_2d(sampler, s, t, p, lodbias, rgba);
+      if (sampler->normalized_coords)
+         sp_get_samples_2d(tgsi_sampler, s, t, p, computeLambda, lodbias, rgba);
       else
-         sp_get_samples_rect(sampler, s, t, p, lodbias, rgba);
+         sp_get_samples_rect(tgsi_sampler, s, t, p, computeLambda, lodbias, rgba);
       break;
    case PIPE_TEXTURE_3D:
-      assert(sampler->state->normalized_coords);
-      sp_get_samples_3d(sampler, s, t, p, lodbias, rgba);
+      assert(sampler->normalized_coords);
+      sp_get_samples_3d(tgsi_sampler, s, t, p, computeLambda, lodbias, rgba);
       break;
    case PIPE_TEXTURE_CUBE:
-      assert(sampler->state->normalized_coords);
-      sp_get_samples_cube(sampler, s, t, p, lodbias, rgba);
+      assert(sampler->normalized_coords);
+      sp_get_samples_cube(tgsi_sampler, s, t, p, computeLambda, lodbias, rgba);
       break;
    default:
       assert(0);
@@ -1077,3 +1254,34 @@ sp_get_samples(struct tgsi_sampler *sampler,
 #endif
 }
 
+
+/**
+ * Called via tgsi_sampler::get_samples() when running a fragment shader.
+ * Get four filtered RGBA values from the sampler's texture.
+ */
+void
+sp_get_samples_fragment(struct tgsi_sampler *tgsi_sampler,
+                        const float s[QUAD_SIZE],
+                        const float t[QUAD_SIZE],
+                        const float p[QUAD_SIZE],
+                        float lodbias,
+                        float rgba[NUM_CHANNELS][QUAD_SIZE])
+{
+   sp_get_samples(tgsi_sampler, s, t, p, TRUE, lodbias, rgba);
+}
+
+
+/**
+ * Called via tgsi_sampler::get_samples() when running a vertex shader.
+ * Get four filtered RGBA values from the sampler's texture.
+ */
+void
+sp_get_samples_vertex(struct tgsi_sampler *tgsi_sampler,
+                      const float s[QUAD_SIZE],
+                      const float t[QUAD_SIZE],
+                      const float p[QUAD_SIZE],
+                      float lodbias,
+                      float rgba[NUM_CHANNELS][QUAD_SIZE])
+{
+   sp_get_samples(tgsi_sampler, s, t, p, FALSE, lodbias, rgba);
+}