gallivm: Implement brilinear filtering.
authorJosé Fonseca <jfonseca@vmware.com>
Fri, 8 Oct 2010 14:50:28 +0000 (15:50 +0100)
committerJosé Fonseca <jfonseca@vmware.com>
Fri, 8 Oct 2010 14:50:28 +0000 (15:50 +0100)
src/gallium/auxiliary/gallivm/lp_bld_sample.c

index 754902891b94e68f3d18dd4db8e9ddf63eac9325..f23ede3319738ae5257322ec50ec5cf52f18fb7e 100644 (file)
 #include "lp_bld_arit.h"
 #include "lp_bld_const.h"
 #include "lp_bld_debug.h"
+#include "lp_bld_printf.h"
 #include "lp_bld_flow.h"
 #include "lp_bld_sample.h"
 #include "lp_bld_swizzle.h"
 #include "lp_bld_type.h"
 
 
+/*
+ * Bri-linear factor. Use zero or any other number less than one to force
+ * tri-linear filtering.
+ */
+#define BRILINEAR_FACTOR 2
+
+
 /**
  * Does the given texture wrap mode allow sampling the texture border color?
  * XXX maybe move this into gallium util code.
@@ -254,6 +262,79 @@ lp_build_rho(struct lp_build_sample_context *bld,
 }
 
 
+/*
+ * Bri-linear lod computation
+ *
+ * Use a piece-wise linear approximation of log2 such that:
+ * - round to nearest, for values in the neighborhood of -1, 0, 1, 2, etc.
+ * - linear approximation for values in the neighborhood of 0.5, 1.5., etc,
+ *   with the steepness specified in 'factor'
+ * - exact result for 0.5, 1.5, etc.
+ *
+ *
+ *   1.0 -              /----*
+ *                     /
+ *                    /
+ *                   /
+ *   0.5 -          *
+ *                 /
+ *                /
+ *               /
+ *   0.0 - *----/
+ *
+ *         |                 |
+ *        2^0               2^1
+ *
+ * This is a technique also commonly used in hardware:
+ * - http://ixbtlabs.com/articles2/gffx/nv40-rx800-3.html
+ *
+ * TODO: For correctness, this should only be applied when texture is known to
+ * have regular mipmaps, i.e., mipmaps derived from the base level.
+ *
+ * TODO: This could be done in fixed point, where applicable.
+ */
+static void
+lp_build_brilinear_lod(struct lp_build_sample_context *bld,
+                       LLVMValueRef lod,
+                       double factor,
+                       LLVMValueRef *out_lod_ipart,
+                       LLVMValueRef *out_lod_fpart)
+{
+   struct lp_build_context *float_bld = &bld->float_bld;
+   LLVMValueRef lod_fpart;
+   float pre_offset = (factor - 0.5)/factor - 0.5;
+   float post_offset = 1 - factor;
+
+   if (0) {
+      lp_build_printf(bld->builder, "lod = %f\n", lod);
+   }
+
+   lod = lp_build_add(float_bld, lod,
+                      lp_build_const_vec(float_bld->type, pre_offset));
+
+   lp_build_ifloor_fract(float_bld, lod, out_lod_ipart, &lod_fpart);
+
+   lod_fpart = lp_build_mul(float_bld, lod_fpart,
+                            lp_build_const_vec(float_bld->type, factor));
+
+   lod_fpart = lp_build_add(float_bld, lod_fpart,
+                            lp_build_const_vec(float_bld->type, post_offset));
+
+   /*
+    * It's not necessary to clamp lod_fpart since:
+    * - the above expression will never produce numbers greater than one.
+    * - the mip filtering branch is only taken if lod_fpart is positive
+    */
+
+   *out_lod_fpart = lod_fpart;
+
+   if (0) {
+      lp_build_printf(bld->builder, "lod_ipart = %i\n", *out_lod_ipart);
+      lp_build_printf(bld->builder, "lod_fpart = %f\n\n", *out_lod_fpart);
+   }
+}
+
+
 /**
  * Generate code to compute texture level of detail (lambda).
  * \param ddx  partial derivatives of (s, t, r, q) with respect to X
@@ -359,7 +440,14 @@ lp_build_lod_selector(struct lp_build_sample_context *bld,
    }
 
    if (mip_filter == PIPE_TEX_MIPFILTER_LINEAR) {
-      lp_build_ifloor_fract(float_bld, lod, out_lod_ipart, out_lod_fpart);
+      if (BRILINEAR_FACTOR > 1.0) {
+         lp_build_brilinear_lod(bld, lod, BRILINEAR_FACTOR,
+                                out_lod_ipart, out_lod_fpart);
+      }
+      else {
+         lp_build_ifloor_fract(float_bld, lod, out_lod_ipart, out_lod_fpart);
+      }
+
       lp_build_name(*out_lod_ipart, "lod_ipart");
       lp_build_name(*out_lod_fpart, "lod_fpart");
    }