Merge commit 'origin/master' into gallium-0.2
[mesa.git] / src / gallium / drivers / softpipe / sp_tex_sample.c
index c0128f81d71627d56f131ee6c8b00278cc1a7354..49250ec084c811e9ec6cb5452faf86c73c4d7e06 100644 (file)
@@ -39,8 +39,9 @@
 #include "sp_tile_cache.h"
 #include "pipe/p_context.h"
 #include "pipe/p_defines.h"
-#include "pipe/p_util.h"
-#include "tgsi/exec/tgsi_exec.h"
+#include "tgsi/tgsi_exec.h"
+#include "util/u_math.h"
+#include "util/u_memory.h"
 
 
 /*
@@ -50,7 +51,7 @@
  * 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))
 
 
 /**
@@ -99,7 +100,7 @@ nearest_texcoord(unsigned wrapMode, float s, unsigned size)
    case PIPE_TEX_WRAP_REPEAT:
       /* s limited to [0,1) */
       /* i limited to [0,size-1] */
-      i = ifloor(s * size);
+      i = util_ifloor(s * size);
       i = REMAINDER(i, size);
       return i;
    case PIPE_TEX_WRAP_CLAMP:
@@ -110,7 +111,7 @@ nearest_texcoord(unsigned wrapMode, float s, unsigned size)
       else if (s >= 1.0F)
          i = size - 1;
       else
-         i = ifloor(s * size);
+         i = util_ifloor(s * size);
       return i;
    case PIPE_TEX_WRAP_CLAMP_TO_EDGE:
       {
@@ -123,7 +124,7 @@ nearest_texcoord(unsigned wrapMode, float s, unsigned size)
          else if (s > max)
             i = size - 1;
          else
-            i = ifloor(s * size);
+            i = util_ifloor(s * size);
       }
       return i;
    case PIPE_TEX_WRAP_CLAMP_TO_BORDER:
@@ -137,14 +138,14 @@ nearest_texcoord(unsigned wrapMode, float s, unsigned size)
          else if (s >= max)
             i = size;
          else
-            i = ifloor(s * size);
+            i = util_ifloor(s * size);
       }
       return i;
    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);
+         const int flr = util_ifloor(s);
          float u;
          if (flr & 1)
             u = 1.0F - (s - (float) flr);
@@ -155,20 +156,20 @@ nearest_texcoord(unsigned wrapMode, float s, unsigned size)
          else if (u > max)
             i = size - 1;
          else
-            i = ifloor(u * size);
+            i = util_ifloor(u * size);
       }
       return i;
    case PIPE_TEX_WRAP_MIRROR_CLAMP:
       {
          /* s limited to [0,1] */
          /* i limited to [0,size-1] */
-         const float u = FABSF(s);
+         const float u = fabsf(s);
          if (u <= 0.0F)
             i = 0;
          else if (u >= 1.0F)
             i = size - 1;
          else
-            i = ifloor(u * size);
+            i = util_ifloor(u * size);
       }
       return i;
    case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE:
@@ -177,13 +178,13 @@ nearest_texcoord(unsigned wrapMode, float s, unsigned size)
          /* 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);
+         const float u = fabsf(s);
          if (u < min)
             i = 0;
          else if (u > max)
             i = size - 1;
          else
-            i = ifloor(u * size);
+            i = util_ifloor(u * size);
       }
       return i;
    case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER:
@@ -192,13 +193,13 @@ nearest_texcoord(unsigned wrapMode, float s, unsigned size)
          /* 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);
+         const float u = fabsf(s);
          if (u < min)
             i = -1;
          else if (u > max)
             i = size;
          else
-            i = ifloor(u * size);
+            i = util_ifloor(u * size);
       }
       return i;
    default:
@@ -225,7 +226,7 @@ linear_texcoord(unsigned wrapMode, float s, unsigned size,
    switch (wrapMode) {
    case PIPE_TEX_WRAP_REPEAT:
       u = s * size - 0.5F;
-      *i0 = REMAINDER(ifloor(u), size);
+      *i0 = REMAINDER(util_ifloor(u), size);
       *i1 = REMAINDER(*i0 + 1, size);
       break;
    case PIPE_TEX_WRAP_CLAMP:
@@ -236,7 +237,7 @@ linear_texcoord(unsigned wrapMode, float s, unsigned size,
       else
          u = s * size;
       u -= 0.5F;
-      *i0 = ifloor(u);
+      *i0 = util_ifloor(u);
       *i1 = *i0 + 1;
       break;
    case PIPE_TEX_WRAP_CLAMP_TO_EDGE:
@@ -247,7 +248,7 @@ linear_texcoord(unsigned wrapMode, float s, unsigned size,
       else
          u = s * size;
       u -= 0.5F;
-      *i0 = ifloor(u);
+      *i0 = util_ifloor(u);
       *i1 = *i0 + 1;
       if (*i0 < 0)
          *i0 = 0;
@@ -265,19 +266,19 @@ linear_texcoord(unsigned wrapMode, float s, unsigned size,
          else
             u = s * size;
          u -= 0.5F;
-         *i0 = ifloor(u);
+         *i0 = util_ifloor(u);
          *i1 = *i0 + 1;
       }
       break;
    case PIPE_TEX_WRAP_MIRROR_REPEAT:
       {
-         const int flr = ifloor(s);
+         const int flr = util_ifloor(s);
          if (flr & 1)
             u = 1.0F - (s - (float) flr);
          else
             u = s - (float) flr;
          u = (u * size) - 0.5F;
-         *i0 = ifloor(u);
+         *i0 = util_ifloor(u);
          *i1 = *i0 + 1;
          if (*i0 < 0)
             *i0 = 0;
@@ -286,23 +287,23 @@ linear_texcoord(unsigned wrapMode, float s, unsigned size,
       }
       break;
    case PIPE_TEX_WRAP_MIRROR_CLAMP:
-      u = FABSF(s);
+      u = fabsf(s);
       if (u >= 1.0F)
          u = (float) size;
       else
          u *= size;
       u -= 0.5F;
-      *i0 = ifloor(u);
+      *i0 = util_ifloor(u);
       *i1 = *i0 + 1;
       break;
    case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE:
-      u = FABSF(s);
+      u = fabsf(s);
       if (u >= 1.0F)
          u = (float) size;
       else
          u *= size;
       u -= 0.5F;
-      *i0 = ifloor(u);
+      *i0 = util_ifloor(u);
       *i1 = *i0 + 1;
       if (*i0 < 0)
          *i0 = 0;
@@ -313,7 +314,7 @@ linear_texcoord(unsigned wrapMode, float s, unsigned size,
       {
          const float min = -1.0F / (2.0F * size);
          const float max = 1.0F - min;
-         u = FABSF(s);
+         u = fabsf(s);
          if (u <= min)
             u = min * size;
          else if (u >= max)
@@ -321,7 +322,7 @@ linear_texcoord(unsigned wrapMode, float s, unsigned size,
          else
             u *= size;
          u -= 0.5F;
-         *i0 = ifloor(u);
+         *i0 = util_ifloor(u);
          *i1 = *i0 + 1;
       }
       break;
@@ -332,6 +333,61 @@ 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)
+{
+   int i;
+   switch (wrapMode) {
+   case PIPE_TEX_WRAP_CLAMP:
+      i = util_ifloor(s);
+      return CLAMP(i, 0, (int) size-1);
+   case PIPE_TEX_WRAP_CLAMP_TO_EDGE:
+      /* fall-through */
+   case PIPE_TEX_WRAP_CLAMP_TO_BORDER:
+      return util_ifloor( CLAMP(s, 0.5F, (float) size - 0.5F) );
+   default:
+      assert(0);
+      return 0;
+   }
+}
+
+
+/**
+ * For RECT textures / unnormalized texcoords.
+ * 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)
+{
+   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 = util_ifloor(s);
+      *i1 = *i0 + 1;
+      break;
+   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 = util_ifloor(s);
+      *i1 = *i0 + 1;
+      if (*i1 > (int) size - 1)
+         *i1 = size - 1;
+      break;
+   default:
+      assert(0);
+   }
+   *a = FRAC(s);
+}
+
+
 static unsigned
 choose_cube_face(float rx, float ry, float rz, float *newS, float *newT)
 {
@@ -346,7 +402,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;
 
@@ -415,40 +471,36 @@ compute_lambda(struct tgsi_sampler *sampler,
 {
    float rho, lambda;
 
+   assert(sampler->state->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);
-      if (sampler->state->normalized_coords)
-         rho *= sampler->texture->width[0];
+      dsdx = fabsf(dsdx);
+      dsdy = fabsf(dsdy);
+      rho = MAX2(dsdx, dsdy) * sampler->texture->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);
-      if (sampler->state->normalized_coords)
-         max *= sampler->texture->height[0];
+      dtdx = fabsf(dtdx);
+      dtdy = fabsf(dtdy);
+      max = MAX2(dtdx, dtdy) * sampler->texture->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);
-      if (sampler->state->normalized_coords)
-         max *= sampler->texture->depth[0];
+      dpdx = fabsf(dpdx);
+      dpdy = fabsf(dpdy);
+      max = MAX2(dpdx, dpdy) * sampler->texture->depth[0];
       rho = MAX2(rho, max);
    }
 
-   lambda = LOG2(rho);
+   lambda = util_fast_log2(rho);
    lambda += lodbias + sampler->state->lod_bias;
    lambda = CLAMP(lambda, sampler->state->min_lod, sampler->state->max_lod);
 
@@ -474,21 +526,24 @@ choose_mipmap_levels(struct tgsi_sampler *sampler,
 {
    if (sampler->state->min_mip_filter == PIPE_TEX_MIPFILTER_NONE) {
       /* no mipmap selection needed */
-      *imgFilter = sampler->state->mag_img_filter;
-      *level0 = *level1 = (int) sampler->state->min_lod;
+      *level0 = *level1 = CLAMP((int) sampler->state->min_lod,
+                                0, (int) sampler->texture->last_level);
 
       if (sampler->state->min_img_filter != sampler->state->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);
-         if (lambda < 0.5) { /* XXX this may need tweaking... */
+         if (lambda <= 0.0) {
             *imgFilter = sampler->state->mag_img_filter;
          }
          else {
             *imgFilter = sampler->state->min_img_filter;
          }
       }
+      else {
+         *imgFilter = sampler->state->mag_img_filter;
+      }
    }
    else {
       float lambda;
@@ -500,7 +555,7 @@ choose_mipmap_levels(struct tgsi_sampler *sampler,
          /* vertex shader */
          lambda = lodbias; /* not really a bias, but absolute LOD */
 
-      if (lambda < 0.0) { /* XXX threshold depends on the filter */
+      if (lambda <= 0.0) { /* XXX threshold depends on the filter */
          /* magnifying */
          *imgFilter = sampler->state->mag_img_filter;
          *level0 = *level1 = 0;
@@ -547,15 +602,31 @@ get_texel(struct tgsi_sampler *sampler,
           unsigned face, unsigned level, int x, int y, int z,
           float rgba[NUM_CHANNELS][QUAD_SIZE], unsigned j)
 {
-   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);
-   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 (x < 0 || x >= (int) sampler->texture->width[level] ||
+       y < 0 || y >= (int) sampler->texture->height[level] ||
+       z < 0 || z >= (int) 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];
+   }
+   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);
+      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(sampler->texture->format));
+      }
+   }
 }
 
 
@@ -597,7 +668,9 @@ shadow_compare(uint compare_func,
       k = 0;
       break;
    default:
+      k = 0;
       assert(0);
+      break;
    }
 
    rgba[0][j] = rgba[1][j] = rgba[2][j] = (float) k;
@@ -625,13 +698,10 @@ sp_get_samples_2d_common(struct tgsi_sampler *sampler,
    choose_mipmap_levels(sampler, s, t, p, lodbias,
                         &level0, &level1, &levelBlend, &imgFilter);
 
-   if (sampler->state->normalized_coords) {
-      width = sampler->texture->width[level0];
-      height = sampler->texture->height[level0];
-   }
-   else {
-      width = height = 1;
-   }
+   assert(sampler->state->normalized_coords);
+
+   width = sampler->texture->width[level0];
+   height = sampler->texture->height[level0];
 
    assert(width > 0);
 
@@ -663,6 +733,7 @@ sp_get_samples_2d_common(struct tgsi_sampler *sampler,
       }
       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;
@@ -762,14 +833,11 @@ sp_get_samples_3d(struct tgsi_sampler *sampler,
    choose_mipmap_levels(sampler, s, t, p, lodbias,
                         &level0, &level1, &levelBlend, &imgFilter);
 
-   if (sampler->state->normalized_coords) {
-      width = sampler->texture->width[level0];
-      height = sampler->texture->height[level0];
-      depth = sampler->texture->depth[level0];
-   }
-   else {
-      width = height = depth = 1;
-   }
+   assert(sampler->state->normalized_coords);
+
+   width = sampler->texture->width[level0];
+   height = sampler->texture->height[level0];
+   depth = sampler->texture->depth[level0];
 
    assert(width > 0);
    assert(height > 0);
@@ -798,6 +866,7 @@ sp_get_samples_3d(struct tgsi_sampler *sampler,
       }
       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 */
@@ -886,6 +955,74 @@ sp_get_samples_cube(struct tgsi_sampler *sampler,
 }
 
 
+static void
+sp_get_samples_rect(struct tgsi_sampler *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_2d_common(sampler, s, t, p, lodbias, rgba, faces);
+   static const uint face = 0;
+   const uint compare_func = sampler->state->compare_func;
+   unsigned level0, level1, j, imgFilter;
+   int width, height;
+   float levelBlend;
+
+   choose_mipmap_levels(sampler, s, t, p, lodbias,
+                        &level0, &level1, &levelBlend, &imgFilter);
+
+   /* texture RECTS cannot be mipmapped */
+   assert(level0 == level1);
+
+   width = sampler->texture->width[level0];
+   height = sampler->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);
+         }
+      }
+      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]);
+         }
+      }
+      break;
+   default:
+      assert(0);
+   }
+}
+
+
+
+
 /**
  * Called via tgsi_sampler::get_samples()
  * Use the sampler's state setting to get a filtered RGBA value
@@ -911,19 +1048,39 @@ sp_get_samples(struct tgsi_sampler *sampler,
 
    switch (sampler->texture->target) {
    case PIPE_TEXTURE_1D:
+      assert(sampler->state->normalized_coords);
       sp_get_samples_1d(sampler, s, t, p, lodbias, rgba);
       break;
    case PIPE_TEXTURE_2D:
-      sp_get_samples_2d(sampler, s, t, p, lodbias, rgba);
+      if (sampler->state->normalized_coords)
+         sp_get_samples_2d(sampler, s, t, p, lodbias, rgba);
+      else
+         sp_get_samples_rect(sampler, s, t, p, lodbias, rgba);
       break;
    case PIPE_TEXTURE_3D:
+      assert(sampler->state->normalized_coords);
       sp_get_samples_3d(sampler, s, t, p, lodbias, rgba);
       break;
    case PIPE_TEXTURE_CUBE:
+      assert(sampler->state->normalized_coords);
       sp_get_samples_cube(sampler, s, t, p, lodbias, rgba);
       break;
    default:
       assert(0);
    }
+
+#if 0 /* DEBUG */
+   {
+      int i;
+      printf("Sampled at %f, %f, %f:\n", s[0], t[0], p[0]);
+      for (i = 0; i < 4; i++) {
+         printf("Frag %d: %f %f %f %f\n", i,
+                rgba[0][i],
+                rgba[1][i],
+                rgba[2][i],
+                rgba[3][i]);
+      }
+   }
+#endif
 }