[Mesa-dev] [PATCH] gallivm: fix out-of-bounds behavior for fetch/ld

sroland at vmware.com sroland at vmware.com
Tue Aug 6 11:51:10 PDT 2013


From: Roland Scheidegger <sroland at vmware.com>

For d3d10 and ARB_robust_buffer_access_behavior, we are required to return
0 for out-of-bounds coordinates (for which we can just enable the code already
there was just disabled). Additionally, also need to return 0 for
out-of-bounds mip level and out-of-bounds layer. This changes the logic
so instead of clamping the level/layer, an out-of-bound mask is computed
instead in this case (actual clamping then can be omitted just like with
coordinates, since we set the fetch offset to zero if that happens anyway).
---
 src/gallium/auxiliary/gallivm/lp_bld_sample.c     |   38 ++++++++--
 src/gallium/auxiliary/gallivm/lp_bld_sample.h     |    3 +-
 src/gallium/auxiliary/gallivm/lp_bld_sample_soa.c |   77 +++++++++++++++------
 3 files changed, 88 insertions(+), 30 deletions(-)

diff --git a/src/gallium/auxiliary/gallivm/lp_bld_sample.c b/src/gallium/auxiliary/gallivm/lp_bld_sample.c
index 573a2d0..a0b1d14 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_sample.c
+++ b/src/gallium/auxiliary/gallivm/lp_bld_sample.c
@@ -47,6 +47,7 @@
 #include "lp_bld_logic.h"
 #include "lp_bld_pack.h"
 #include "lp_bld_quad.h"
+#include "lp_bld_bitarit.h"
 
 
 /*
@@ -777,17 +778,19 @@ lp_build_lod_selector(struct lp_build_sample_context *bld,
 
 
 /**
- * For PIPE_TEX_MIPFILTER_NEAREST, convert float LOD to integer
- * mipmap level index.
+ * For PIPE_TEX_MIPFILTER_NEAREST, convert int part of lod
+ * to actual mip level.
  * Note: this is all scalar per quad code.
  * \param lod_ipart  int texture level of detail
- * \param level_out  returns integer 
+ * \param level_out  returns integer
+ * \param out_of_bounds returns per coord out_of_bounds mask if provided
  */
 void
 lp_build_nearest_mip_level(struct lp_build_sample_context *bld,
                            unsigned texture_unit,
                            LLVMValueRef lod_ipart,
-                           LLVMValueRef *level_out)
+                           LLVMValueRef *level_out,
+                           LLVMValueRef *out_of_bounds)
 {
    struct lp_build_context *leveli_bld = &bld->leveli_bld;
    LLVMValueRef first_level, last_level, level;
@@ -801,8 +804,31 @@ lp_build_nearest_mip_level(struct lp_build_sample_context *bld,
 
    level = lp_build_add(leveli_bld, lod_ipart, first_level);
 
-   /* clamp level to legal range of levels */
-   *level_out = lp_build_clamp(leveli_bld, level, first_level, last_level);
+   if (out_of_bounds) {
+      LLVMValueRef out, out1;
+      out = lp_build_cmp(leveli_bld, PIPE_FUNC_LESS, level, first_level);
+      out1 = lp_build_cmp(leveli_bld, PIPE_FUNC_GREATER, level, last_level);
+      out = lp_build_or(leveli_bld, out, out1);
+      if (bld->num_lods == bld->coord_bld.type.length) {
+         *out_of_bounds = out;
+      }
+      else if (bld->num_lods == 1) {
+         *out_of_bounds = lp_build_broadcast_scalar(&bld->int_coord_bld, out);
+      }
+      else {
+         assert(bld->num_lods == bld->coord_bld.type.length / 4);
+         *out_of_bounds = lp_build_unpack_broadcast_aos_scalars(bld->gallivm,
+                                                                leveli_bld->type,
+                                                                bld->int_coord_bld.type,
+                                                                out);
+      }
+      *level_out = level;
+   }
+   else {
+      /* clamp level to legal range of levels */
+      *level_out = lp_build_clamp(leveli_bld, level, first_level, last_level);
+
+   }
 }
 
 
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_sample.h b/src/gallium/auxiliary/gallivm/lp_bld_sample.h
index a3ecc05..f9a2b3f 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_sample.h
+++ b/src/gallium/auxiliary/gallivm/lp_bld_sample.h
@@ -382,7 +382,8 @@ void
 lp_build_nearest_mip_level(struct lp_build_sample_context *bld,
                            unsigned texture_unit,
                            LLVMValueRef lod,
-                           LLVMValueRef *level_out);
+                           LLVMValueRef *level_out,
+                           LLVMValueRef *out_of_bounds);
 
 void
 lp_build_linear_mip_levels(struct lp_build_sample_context *bld,
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_sample_soa.c b/src/gallium/auxiliary/gallivm/lp_bld_sample_soa.c
index fa9edb5..122ec4f 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_sample_soa.c
+++ b/src/gallium/auxiliary/gallivm/lp_bld_sample_soa.c
@@ -1055,22 +1055,36 @@ lp_build_sample_mipmap(struct lp_build_sample_context *bld,
 
 
 /**
- * Clamp layer coord to valid values.
+ * Build (per-coord) layer value.
+ * Either clamp layer to valid values or fill in optional out_of_bounds
+ * value and just return value unclamped.
  */
 static LLVMValueRef
 lp_build_layer_coord(struct lp_build_sample_context *bld,
                      unsigned texture_unit,
-                     LLVMValueRef layer)
+                     LLVMValueRef layer,
+                     LLVMValueRef *out_of_bounds)
 {
-   LLVMValueRef maxlayer;
+   LLVMValueRef num_layers;
+   struct lp_build_context *int_coord_bld = &bld->int_coord_bld;
 
-   maxlayer = bld->dynamic_state->depth(bld->dynamic_state,
-                                        bld->gallivm, texture_unit);
-   maxlayer = lp_build_sub(&bld->int_bld, maxlayer, bld->int_bld.one);
-   maxlayer = lp_build_broadcast_scalar(&bld->int_coord_bld, maxlayer);
-   return lp_build_clamp(&bld->int_coord_bld, layer,
-                         bld->int_coord_bld.zero, maxlayer);
+   num_layers = bld->dynamic_state->depth(bld->dynamic_state,
+                                          bld->gallivm, texture_unit);
 
+   if (out_of_bounds) {
+      LLVMValueRef out1, out;
+      num_layers = lp_build_broadcast_scalar(int_coord_bld, num_layers);
+      out = lp_build_cmp(int_coord_bld, PIPE_FUNC_LESS, layer, int_coord_bld->zero);
+      out1 = lp_build_cmp(int_coord_bld, PIPE_FUNC_GEQUAL, layer, num_layers);
+      *out_of_bounds = lp_build_or(int_coord_bld, out, out1);
+      return layer;
+   }
+   else {
+      LLVMValueRef maxlayer;
+      maxlayer = lp_build_sub(&bld->int_bld, num_layers, bld->int_bld.one);
+      maxlayer = lp_build_broadcast_scalar(int_coord_bld, maxlayer);
+      return lp_build_clamp(int_coord_bld, layer, int_coord_bld->zero, maxlayer);
+   }
 }
 
 
@@ -1123,11 +1137,11 @@ lp_build_sample_common(struct lp_build_sample_context *bld,
    }
    else if (target == PIPE_TEXTURE_1D_ARRAY) {
       *r = lp_build_iround(&bld->coord_bld, *t);
-      *r = lp_build_layer_coord(bld, texture_index, *r);
+      *r = lp_build_layer_coord(bld, texture_index, *r, NULL);
    }
    else if (target == PIPE_TEXTURE_2D_ARRAY) {
       *r = lp_build_iround(&bld->coord_bld, *r);
-      *r = lp_build_layer_coord(bld, texture_index, *r);
+      *r = lp_build_layer_coord(bld, texture_index, *r, NULL);
    }
 
    /*
@@ -1162,7 +1176,7 @@ lp_build_sample_common(struct lp_build_sample_context *bld,
           * bad x86 code to be emitted.
           */
          assert(*lod_ipart);
-         lp_build_nearest_mip_level(bld, texture_index, *lod_ipart, ilevel0);
+         lp_build_nearest_mip_level(bld, texture_index, *lod_ipart, ilevel0, NULL);
       }
       else {
          first_level = bld->dynamic_state->first_level(bld->dynamic_state,
@@ -1173,7 +1187,7 @@ lp_build_sample_common(struct lp_build_sample_context *bld,
       break;
    case PIPE_TEX_MIPFILTER_NEAREST:
       assert(*lod_ipart);
-      lp_build_nearest_mip_level(bld, texture_index, *lod_ipart, ilevel0);
+      lp_build_nearest_mip_level(bld, texture_index, *lod_ipart, ilevel0, NULL);
       break;
    case PIPE_TEX_MIPFILTER_LINEAR:
       assert(*lod_ipart);
@@ -1300,12 +1314,15 @@ lp_build_fetch_texel(struct lp_build_sample_context *bld,
    struct lp_build_context *int_coord_bld = &bld->int_coord_bld;
    unsigned dims = bld->dims, chan;
    unsigned target = bld->static_texture_state->target;
+   boolean out_of_bound_ret_zero = TRUE;
    LLVMValueRef size, ilevel;
    LLVMValueRef row_stride_vec = NULL, img_stride_vec = NULL;
    LLVMValueRef x = coords[0], y = coords[1], z = coords[2];
    LLVMValueRef width, height, depth, i, j;
    LLVMValueRef offset, out_of_bounds, out1;
 
+   out_of_bounds = int_coord_bld->zero;
+
    if (explicit_lod && bld->static_texture_state->target != PIPE_BUFFER) {
       if (bld->num_lods != int_coord_bld->type.length) {
          ilevel = lp_build_pack_aos_scalars(bld->gallivm, int_coord_bld->type,
@@ -1314,11 +1331,18 @@ lp_build_fetch_texel(struct lp_build_sample_context *bld,
       else {
          ilevel = explicit_lod;
       }
-      lp_build_nearest_mip_level(bld, texture_unit, ilevel, &ilevel);
+      lp_build_nearest_mip_level(bld, texture_unit, ilevel, &ilevel,
+                                 out_of_bound_ret_zero ? &out_of_bounds : NULL);
    }
    else {
-      bld->num_lods = 1;
-      ilevel = lp_build_const_int32(bld->gallivm, 0);
+      assert(bld->num_lods == 1);
+      if (bld->static_texture_state->target != PIPE_BUFFER) {
+         ilevel = bld->dynamic_state->first_level(bld->dynamic_state,
+                                                  bld->gallivm, texture_unit);
+      }
+      else {
+         ilevel = lp_build_const_int32(bld->gallivm, 0);
+      }
    }
    lp_build_mipmap_level_sizes(bld, ilevel,
                                &size,
@@ -1329,19 +1353,27 @@ lp_build_fetch_texel(struct lp_build_sample_context *bld,
    if (target == PIPE_TEXTURE_1D_ARRAY ||
        target == PIPE_TEXTURE_2D_ARRAY) {
       if (target == PIPE_TEXTURE_1D_ARRAY) {
-         z = lp_build_layer_coord(bld, texture_unit, y);
+         z = y;
+      }
+      if (out_of_bound_ret_zero) {
+         z = lp_build_layer_coord(bld, texture_unit, z, &out1);
+         out_of_bounds = lp_build_or(int_coord_bld, out_of_bounds, out1);
       }
       else {
-         z = lp_build_layer_coord(bld, texture_unit, z);
+         z = lp_build_layer_coord(bld, texture_unit, z, NULL);
       }
    }
 
    /* This is a lot like border sampling */
    if (offsets[0]) {
-      /* XXX coords are really unsigned, offsets are signed */
+      /*
+       * coords are really unsigned, offsets are signed, but I don't think
+       * exceeding 31 bits is possible
+       */
       x = lp_build_add(int_coord_bld, x, offsets[0]);
    }
-   out_of_bounds = lp_build_cmp(int_coord_bld, PIPE_FUNC_LESS, x, int_coord_bld->zero);
+   out1 = lp_build_cmp(int_coord_bld, PIPE_FUNC_LESS, x, int_coord_bld->zero);
+   out_of_bounds = lp_build_or(int_coord_bld, out_of_bounds, out1);
    out1 = lp_build_cmp(int_coord_bld, PIPE_FUNC_GEQUAL, x, width);
    out_of_bounds = lp_build_or(int_coord_bld, out_of_bounds, out1);
 
@@ -1384,11 +1416,10 @@ lp_build_fetch_texel(struct lp_build_sample_context *bld,
                            i, j,
                            colors_out);
 
-   if (0) {
+   if (out_of_bound_ret_zero) {
       /*
-       * Not needed except for ARB_robust_buffer_access_behavior.
+       * Only needed for ARB_robust_buffer_access_behavior and d3d10.
        * Could use min/max above instead of out-of-bounds comparisons
-       * (in fact cast to unsigned and min only is sufficient)
        * if we don't care about the result returned for out-of-bounds.
        */
       for (chan = 0; chan < 4; chan++) {
-- 
1.7.9.5


More information about the mesa-dev mailing list