nir/lower_tex: add support to clamp texture coords
authorRob Clark <robclark@freedesktop.org>
Fri, 18 Sep 2015 14:44:27 +0000 (10:44 -0400)
committerRob Clark <robclark@freedesktop.org>
Sat, 19 Sep 2015 01:07:49 +0000 (21:07 -0400)
Some hardware needs to clamp texture coordinates to [0.0, 1.0] in the
shader to emulate GL_CLAMP.  This is added to lower_tex_proj since, in
the case of projected coords, the clamping needs to happen *after*
projection.

v2: comments/suggestions from Ilia and Eric, use txs to get texture size
and clamp RECT textures to their dimensions rather than [0.0, 1.0] to
avoid having to lower RECT textures to 2D.

Signed-off-by: Rob Clark <robclark@freedesktop.org>
Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
src/glsl/nir/nir.h
src/glsl/nir/nir_lower_tex.c

index 3c908b9f295e5873ebac96aace1671cc8a1d1883..255d45585db21e83662a5b4e3c25b438f4087067 100644 (file)
@@ -1850,6 +1850,24 @@ typedef struct nir_lower_tex_options {
     * texture dims to normalize.
     */
    bool lower_rect;
+
+   /**
+    * To emulate certain texture wrap modes, this can be used
+    * to saturate the specified tex coord to [0.0, 1.0].  The
+    * bits are according to sampler #, ie. if, for example:
+    *
+    *   (conf->saturate_s & (1 << n))
+    *
+    * is true, then the s coord for sampler n is saturated.
+    *
+    * Note that clamping must happen *after* projector lowering
+    * so any projected texture sample instruction with a clamped
+    * coordinate gets automatically lowered, regardless of the
+    * 'lower_txp' setting.
+    */
+   unsigned saturate_s;
+   unsigned saturate_t;
+   unsigned saturate_r;
 } nir_lower_tex_options;
 
 void nir_lower_tex(nir_shader *shader,
index 63f51bcbdc539fca9f7a8eb0227ce9b96ce59021..e2f095a553253a2c0d3c76cb6d44fa3366acbcb1 100644 (file)
  *     asking the texture operation to do so.
  *   + lowering RECT: converts the un-normalized RECT texture coordinates
  *     to normalized coordinates with txs plus ALU instructions
+ *   + saturate s/t/r coords: to emulate certain texture clamp/wrap modes,
+ *     inserts instructions to clamp specified coordinates to [0.0, 1.0].
+ *     Note that this automatically triggers texture projector lowering if
+ *     needed, since clamping must happen after projector lowering.
  */
 
 #include "nir.h"
@@ -161,6 +165,70 @@ lower_rect(nir_builder *b, nir_tex_instr *tex)
    tex->sampler_dim = GLSL_SAMPLER_DIM_2D;
 }
 
+static void
+saturate_src(nir_builder *b, nir_tex_instr *tex, unsigned sat_mask)
+{
+   b->cursor = nir_before_instr(&tex->instr);
+
+   /* Walk through the sources saturating the requested arguments. */
+   for (unsigned i = 0; i < tex->num_srcs; i++) {
+      if (tex->src[i].src_type != nir_tex_src_coord)
+         continue;
+
+      nir_ssa_def *src =
+         nir_ssa_for_src(b, tex->src[i].src, tex->coord_components);
+
+      /* split src into components: */
+      nir_ssa_def *comp[4];
+
+      for (unsigned j = 0; j < tex->coord_components; j++)
+         comp[j] = nir_channel(b, src, j);
+
+      /* clamp requested components, array index does not get clamped: */
+      unsigned ncomp = tex->coord_components;
+      if (tex->is_array)
+         ncomp--;
+
+      for (unsigned j = 0; j < ncomp; j++) {
+         if ((1 << j) & sat_mask) {
+            if (tex->sampler_dim == GLSL_SAMPLER_DIM_RECT) {
+               /* non-normalized texture coords, so clamp to texture
+                * size rather than [0.0, 1.0]
+                */
+               nir_ssa_def *txs = get_texture_size(b, tex);
+               comp[j] = nir_fmax(b, comp[j], nir_imm_float(b, 0.0));
+               comp[j] = nir_fmin(b, comp[j], nir_channel(b, txs, j));
+            } else {
+               comp[j] = nir_fsat(b, comp[j]);
+            }
+         }
+      }
+
+      /* and move the result back into a single vecN: */
+      switch (tex->coord_components) {
+      case 4:
+         src = nir_vec4(b, comp[0], comp[1], comp[2], comp[3]);
+         break;
+      case 3:
+         src = nir_vec3(b, comp[0], comp[1], comp[2]);
+         break;
+      case 2:
+         src = nir_vec2(b, comp[0], comp[1]);
+         break;
+      case 1:
+         src = comp[0];
+         break;
+      default:
+         unreachable("bad texture coord count");
+         break;
+      }
+
+      nir_instr_rewrite_src(&tex->instr,
+                            &tex->src[i].src,
+                            nir_src_for_ssa(src));
+   }
+}
+
 static bool
 nir_lower_tex_block(nir_block *block, void *void_state)
 {
@@ -174,12 +242,28 @@ nir_lower_tex_block(nir_block *block, void *void_state)
       nir_tex_instr *tex = nir_instr_as_tex(instr);
       bool lower_txp = !!(state->options->lower_txp & (1 << tex->sampler_dim));
 
-      if (lower_txp)
+      /* mask of src coords to saturate (clamp): */
+      unsigned sat_mask = 0;
+
+      if ((1 << tex->sampler_index) & state->options->saturate_r)
+         sat_mask |= (1 << 2);    /* .z */
+      if ((1 << tex->sampler_index) & state->options->saturate_t)
+         sat_mask |= (1 << 1);    /* .y */
+      if ((1 << tex->sampler_index) & state->options->saturate_s)
+         sat_mask |= (1 << 0);    /* .x */
+
+      /* If we are clamping any coords, we must lower projector first
+       * as clamping happens *after* projection:
+       */
+      if (lower_txp || sat_mask)
          project_src(b, tex);
 
       if ((tex->sampler_dim == GLSL_SAMPLER_DIM_RECT) &&
           state->options->lower_rect)
          lower_rect(b, tex);
+
+      if (sat_mask)
+         saturate_src(b, tex, sat_mask);
    }
 
    return true;