gallivm: do per-pixel cube face selection (finally!!!)
[mesa.git] / src / gallium / auxiliary / gallivm / lp_bld_sample.c
index 1ea59ea222c17f1173e6bc80e1c851ae8baf01f2..5d5092155cf9c9e2977120788b6e08ff77339a34 100644 (file)
@@ -46,6 +46,7 @@
 #include "lp_bld_type.h"
 #include "lp_bld_logic.h"
 #include "lp_bld_pack.h"
+#include "lp_bld_quad.h"
 
 
 /*
@@ -87,23 +88,53 @@ lp_sampler_wrap_mode_uses_border_color(unsigned mode,
 
 
 /**
- * Initialize lp_sampler_static_state object with the gallium sampler
- * and texture state.
- * The former is considered to be static and the later dynamic.
+ * Initialize lp_sampler_static_texture_state object with the gallium
+ * texture/sampler_view state (this contains the parts which are
+ * considered static).
  */
 void
-lp_sampler_static_state(struct lp_sampler_static_state *state,
-                        const struct pipe_sampler_view *view,
-                        const struct pipe_sampler_state *sampler)
+lp_sampler_static_texture_state(struct lp_static_texture_state *state,
+                                const struct pipe_sampler_view *view)
 {
-   const struct pipe_resource *texture = view->texture;
+   const struct pipe_resource *texture;
 
    memset(state, 0, sizeof *state);
 
-   if(!texture)
+   if (!view || !view->texture)
       return;
 
-   if(!sampler)
+   texture = view->texture;
+
+   state->format            = view->format;
+   state->swizzle_r         = view->swizzle_r;
+   state->swizzle_g         = view->swizzle_g;
+   state->swizzle_b         = view->swizzle_b;
+   state->swizzle_a         = view->swizzle_a;
+
+   state->target            = texture->target;
+   state->pot_width         = util_is_power_of_two(texture->width0);
+   state->pot_height        = util_is_power_of_two(texture->height0);
+   state->pot_depth         = util_is_power_of_two(texture->depth0);
+   state->level_zero_only   = !view->u.tex.last_level;
+
+   /*
+    * the layer / element / level parameters are all either dynamic
+    * state or handled transparently wrt execution.
+    */
+}
+
+
+/**
+ * Initialize lp_sampler_static_sampler_state object with the gallium sampler
+ * state (this contains the parts which are considered static).
+ */
+void
+lp_sampler_static_sampler_state(struct lp_static_sampler_state *state,
+                                const struct pipe_sampler_state *sampler)
+{
+   memset(state, 0, sizeof *state);
+
+   if (!sampler)
       return;
 
    /*
@@ -118,24 +149,13 @@ lp_sampler_static_state(struct lp_sampler_static_state *state,
     * regarding 1D/2D/3D/CUBE textures, wrap modes, etc.
     */
 
-   state->format            = view->format;
-   state->swizzle_r         = view->swizzle_r;
-   state->swizzle_g         = view->swizzle_g;
-   state->swizzle_b         = view->swizzle_b;
-   state->swizzle_a         = view->swizzle_a;
-
-   state->target            = texture->target;
-   state->pot_width         = util_is_power_of_two(texture->width0);
-   state->pot_height        = util_is_power_of_two(texture->height0);
-   state->pot_depth         = util_is_power_of_two(texture->depth0);
-
    state->wrap_s            = sampler->wrap_s;
    state->wrap_t            = sampler->wrap_t;
    state->wrap_r            = sampler->wrap_r;
    state->min_img_filter    = sampler->min_img_filter;
    state->mag_img_filter    = sampler->mag_img_filter;
 
-   if (view->u.tex.last_level && sampler->max_lod > 0.0f) {
+   if (sampler->max_lod > 0.0f) {
       state->min_mip_filter = sampler->min_mip_filter;
    } else {
       state->min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
@@ -156,7 +176,11 @@ lp_sampler_static_state(struct lp_sampler_static_state *state,
             state->apply_min_lod = 1;
          }
 
-         if (sampler->max_lod < (float)view->u.tex.last_level) {
+         /*
+          * XXX this won't do anything with the mesa state tracker which always
+          * sets max_lod to not more than actually present mip maps...
+          */
+         if (sampler->max_lod < (PIPE_MAX_TEXTURE_LEVELS - 1)) {
             state->apply_max_lod = 1;
          }
       }
@@ -168,10 +192,6 @@ lp_sampler_static_state(struct lp_sampler_static_state *state,
    }
 
    state->normalized_coords = sampler->normalized_coords;
-
-   /*
-    * FIXME: Handle the remainder of pipe_sampler_view.
-    */
 }
 
 
@@ -183,17 +203,21 @@ lp_sampler_static_state(struct lp_sampler_static_state *state,
  */
 static LLVMValueRef
 lp_build_rho(struct lp_build_sample_context *bld,
-             unsigned unit,
+             unsigned texture_unit,
+             LLVMValueRef s,
+             LLVMValueRef t,
+             LLVMValueRef r,
+             LLVMValueRef cube_rho,
              const struct lp_derivatives *derivs)
 {
    struct gallivm_state *gallivm = bld->gallivm;
-   struct lp_build_context *int_size_bld = &bld->int_size_bld;
-   struct lp_build_context *float_size_bld = &bld->float_size_bld;
+   struct lp_build_context *int_size_bld = &bld->int_size_in_bld;
+   struct lp_build_context *float_size_bld = &bld->float_size_in_bld;
    struct lp_build_context *float_bld = &bld->float_bld;
    struct lp_build_context *coord_bld = &bld->coord_bld;
    struct lp_build_context *perquadf_bld = &bld->perquadf_bld;
-   const LLVMValueRef *ddx_ddy = derivs->ddx_ddy;
    const unsigned dims = bld->dims;
+   LLVMValueRef ddx_ddy[2];
    LLVMBuilderRef builder = bld->gallivm->builder;
    LLVMTypeRef i32t = LLVMInt32TypeInContext(bld->gallivm->context);
    LLVMValueRef index0 = LLVMConstInt(i32t, 0, 0);
@@ -203,143 +227,200 @@ lp_build_rho(struct lp_build_sample_context *bld,
    LLVMValueRef int_size, float_size;
    LLVMValueRef rho;
    LLVMValueRef first_level, first_level_vec;
-   LLVMValueRef abs_ddx_ddy[2];
    unsigned length = coord_bld->type.length;
    unsigned num_quads = length / 4;
    unsigned i;
    LLVMValueRef i32undef = LLVMGetUndef(LLVMInt32TypeInContext(gallivm->context));
    LLVMValueRef rho_xvec, rho_yvec;
 
-   abs_ddx_ddy[0] = lp_build_abs(coord_bld, ddx_ddy[0]);
-   if (dims > 2) {
-      abs_ddx_ddy[1] = lp_build_abs(coord_bld, ddx_ddy[1]);
-   }
-   else {
-      abs_ddx_ddy[1] = NULL;
-   }
-
-   if (dims == 1) {
-      static const unsigned char swizzle1[] = {
-         0, LP_BLD_SWIZZLE_DONTCARE,
-         LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
-      };
-      static const unsigned char swizzle2[] = {
-         1, LP_BLD_SWIZZLE_DONTCARE,
-         LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
-      };
-      rho_xvec = lp_build_swizzle_aos(coord_bld, abs_ddx_ddy[0], swizzle1);
-      rho_yvec = lp_build_swizzle_aos(coord_bld, abs_ddx_ddy[0], swizzle2);
-   }
-   else if (dims == 2) {
-      static const unsigned char swizzle1[] = {
-         0, 2,
-         LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
-      };
-      static const unsigned char swizzle2[] = {
-         1, 3,
-         LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
-      };
-      rho_xvec = lp_build_swizzle_aos(coord_bld, abs_ddx_ddy[0], swizzle1);
-      rho_yvec = lp_build_swizzle_aos(coord_bld, abs_ddx_ddy[0], swizzle2);
-   }
-   else {
-      LLVMValueRef shuffles1[LP_MAX_VECTOR_LENGTH];
-      LLVMValueRef shuffles2[LP_MAX_VECTOR_LENGTH];
-      assert(dims == 3);
-      for (i = 0; i < num_quads; i++) {
-         shuffles1[4*i + 0] = lp_build_const_int32(gallivm, 4*i);
-         shuffles1[4*i + 1] = lp_build_const_int32(gallivm, 4*i + 2);
-         shuffles1[4*i + 2] = lp_build_const_int32(gallivm, length + 4*i);
-         shuffles1[4*i + 3] = i32undef;
-         shuffles2[4*i + 0] = lp_build_const_int32(gallivm, 4*i + 1);
-         shuffles2[4*i + 1] = lp_build_const_int32(gallivm, 4*i + 3);
-         shuffles2[4*i + 2] = lp_build_const_int32(gallivm, length + 4*i + 1);
-         shuffles2[4*i + 3] = i32undef;
-      }
-      rho_xvec = LLVMBuildShuffleVector(builder, abs_ddx_ddy[0], abs_ddx_ddy[1],
-                                        LLVMConstVector(shuffles1, length), "");
-      rho_yvec = LLVMBuildShuffleVector(builder, abs_ddx_ddy[0], abs_ddx_ddy[1],
-                                        LLVMConstVector(shuffles2, length), "");
-   }
-
-   rho_vec = lp_build_max(coord_bld, rho_xvec, rho_yvec);
+   /* Note that all simplified calculations will only work for isotropic filtering */
 
    first_level = bld->dynamic_state->first_level(bld->dynamic_state,
-                                                 bld->gallivm, unit);
-   first_level_vec = lp_build_broadcast_scalar(&bld->int_size_bld, first_level);
+                                                 bld->gallivm, texture_unit);
+   first_level_vec = lp_build_broadcast_scalar(int_size_bld, first_level);
    int_size = lp_build_minify(int_size_bld, bld->int_size, first_level_vec);
    float_size = lp_build_int_to_float(float_size_bld, int_size);
 
-   if (bld->coord_type.length > 4) {
-      /* expand size to each quad */
+   if (cube_rho) {
+      LLVMValueRef cubesize;
+      LLVMValueRef index0 = lp_build_const_int32(gallivm, 0);
+      /*
+       * If we have derivs too then we have per-pixel cube_rho - doesn't matter
+       * though until we do per-pixel lod.
+       * Cube map code did already everything except size mul and per-quad extraction.
+       */
+      /* Could optimize this for single quad just skip the broadcast */
+      cubesize = lp_build_extract_broadcast(gallivm, bld->float_size_in_type,
+                                            coord_bld->type, float_size, index0);
+      rho_vec = lp_build_mul(coord_bld, cubesize, cube_rho);
+      rho = lp_build_pack_aos_scalars(bld->gallivm, coord_bld->type,
+                                      perquadf_bld->type, rho_vec, 0);
+   }
+   else if (derivs && !(bld->static_texture_state->target == PIPE_TEXTURE_CUBE)) {
+      LLVMValueRef ddmax[3];
+      for (i = 0; i < dims; i++) {
+         LLVMValueRef ddx, ddy;
+         LLVMValueRef floatdim;
+         LLVMValueRef indexi = lp_build_const_int32(gallivm, i);
+         ddx = lp_build_abs(coord_bld, derivs->ddx[i]);
+         ddy = lp_build_abs(coord_bld, derivs->ddy[i]);
+         ddmax[i] = lp_build_max(coord_bld, ddx, ddy);
+         floatdim = lp_build_extract_broadcast(gallivm, bld->float_size_in_type,
+                                               coord_bld->type, float_size, indexi);
+         ddmax[i] = lp_build_mul(coord_bld, floatdim, ddmax[i]);
+      }
+      rho_vec = ddmax[0];
       if (dims > 1) {
-         /* could use some broadcast_vector helper for this? */
-         int num_quads = bld->coord_type.length / 4;
-         LLVMValueRef src[LP_MAX_VECTOR_LENGTH/4];
-         for (i = 0; i < num_quads; i++) {
-            src[i] = float_size;
+         rho_vec = lp_build_max(coord_bld, rho_vec, ddmax[1]);
+         if (dims > 2) {
+            rho_vec = lp_build_max(coord_bld, rho_vec, ddmax[2]);
          }
-         float_size = lp_build_concat(bld->gallivm, src, float_size_bld->type, num_quads);
       }
-      else {
-         float_size = lp_build_broadcast_scalar(coord_bld, float_size);
+      /*
+       * rho_vec now still contains per-pixel rho, convert to scalar per quad
+       * since we can't handle per-pixel rho/lod from now on (TODO).
+       */
+      rho = lp_build_pack_aos_scalars(bld->gallivm, coord_bld->type,
+                                      perquadf_bld->type, rho_vec, 0);
+   }
+   else {
+      /*
+       * This looks all a bit complex, but it's not that bad
+       * (the shuffle code makes it look worse than it is).
+       * Still, might not be ideal for all cases.
+       */
+      if (dims < 2) {
+         ddx_ddy[0] = lp_build_packed_ddx_ddy_onecoord(coord_bld, s);
+      }
+      else if (dims >= 2) {
+         ddx_ddy[0] = lp_build_packed_ddx_ddy_twocoord(coord_bld, s, t);
+         if (dims > 2) {
+            ddx_ddy[1] = lp_build_packed_ddx_ddy_onecoord(coord_bld, r);
+         }
       }
-      rho_vec = lp_build_mul(coord_bld, rho_vec, float_size);
 
-      if (dims <= 1) {
-         rho = rho_vec;
+      ddx_ddy[0] = lp_build_abs(coord_bld, ddx_ddy[0]);
+      if (dims > 2) {
+         ddx_ddy[1] = lp_build_abs(coord_bld, ddx_ddy[1]);
+      }
+
+      if (dims < 2) {
+         static const unsigned char swizzle1[] = { /* no-op swizzle */
+            0, LP_BLD_SWIZZLE_DONTCARE,
+            LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
+         };
+         static const unsigned char swizzle2[] = {
+            2, LP_BLD_SWIZZLE_DONTCARE,
+            LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
+         };
+         rho_xvec = lp_build_swizzle_aos(coord_bld, ddx_ddy[0], swizzle1);
+         rho_yvec = lp_build_swizzle_aos(coord_bld, ddx_ddy[0], swizzle2);
+      }
+      else if (dims == 2) {
+         static const unsigned char swizzle1[] = {
+            0, 2,
+            LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
+         };
+         static const unsigned char swizzle2[] = {
+            1, 3,
+            LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
+         };
+         rho_xvec = lp_build_swizzle_aos(coord_bld, ddx_ddy[0], swizzle1);
+         rho_yvec = lp_build_swizzle_aos(coord_bld, ddx_ddy[0], swizzle2);
       }
       else {
-         if (dims >= 2) {
-            static const unsigned char swizzle1[] = {
-               0, LP_BLD_SWIZZLE_DONTCARE,
-               LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
-            };
-            static const unsigned char swizzle2[] = {
-               1, LP_BLD_SWIZZLE_DONTCARE,
-               LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
-            };
-            LLVMValueRef rho_s, rho_t, rho_r;
-
-            rho_s = lp_build_swizzle_aos(coord_bld, rho_vec, swizzle1);
-            rho_t = lp_build_swizzle_aos(coord_bld, rho_vec, swizzle2);
-
-            rho = lp_build_max(coord_bld, rho_s, rho_t);
-
-            if (dims >= 3) {
-               static const unsigned char swizzle3[] = {
-                  2, LP_BLD_SWIZZLE_DONTCARE,
+         LLVMValueRef shuffles1[LP_MAX_VECTOR_LENGTH];
+         LLVMValueRef shuffles2[LP_MAX_VECTOR_LENGTH];
+         assert(dims == 3);
+         for (i = 0; i < num_quads; i++) {
+            shuffles1[4*i + 0] = lp_build_const_int32(gallivm, 4*i);
+            shuffles1[4*i + 1] = lp_build_const_int32(gallivm, 4*i + 2);
+            shuffles1[4*i + 2] = lp_build_const_int32(gallivm, length + 4*i);
+            shuffles1[4*i + 3] = i32undef;
+            shuffles2[4*i + 0] = lp_build_const_int32(gallivm, 4*i + 1);
+            shuffles2[4*i + 1] = lp_build_const_int32(gallivm, 4*i + 3);
+            shuffles2[4*i + 2] = lp_build_const_int32(gallivm, length + 4*i + 2);
+            shuffles2[4*i + 3] = i32undef;
+         }
+         rho_xvec = LLVMBuildShuffleVector(builder, ddx_ddy[0], ddx_ddy[1],
+                                           LLVMConstVector(shuffles1, length), "");
+         rho_yvec = LLVMBuildShuffleVector(builder, ddx_ddy[0], ddx_ddy[1],
+                                           LLVMConstVector(shuffles2, length), "");
+      }
+
+      rho_vec = lp_build_max(coord_bld, rho_xvec, rho_yvec);
+
+      if (bld->coord_type.length > 4) {
+         /* expand size to each quad */
+         if (dims > 1) {
+            /* could use some broadcast_vector helper for this? */
+            int num_quads = bld->coord_type.length / 4;
+            LLVMValueRef src[LP_MAX_VECTOR_LENGTH/4];
+            for (i = 0; i < num_quads; i++) {
+               src[i] = float_size;
+            }
+            float_size = lp_build_concat(bld->gallivm, src, float_size_bld->type, num_quads);
+         }
+         else {
+            float_size = lp_build_broadcast_scalar(coord_bld, float_size);
+         }
+         rho_vec = lp_build_mul(coord_bld, rho_vec, float_size);
+
+         if (dims <= 1) {
+            rho = rho_vec;
+         }
+         else {
+            if (dims >= 2) {
+               static const unsigned char swizzle1[] = {
+                  0, LP_BLD_SWIZZLE_DONTCARE,
+                  LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
+               };
+               static const unsigned char swizzle2[] = {
+                  1, LP_BLD_SWIZZLE_DONTCARE,
                   LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
                };
-               rho_r = lp_build_swizzle_aos(coord_bld, rho_vec, swizzle3);
-               rho = lp_build_max(coord_bld, rho, rho_r);
+               LLVMValueRef rho_s, rho_t, rho_r;
+
+               rho_s = lp_build_swizzle_aos(coord_bld, rho_vec, swizzle1);
+               rho_t = lp_build_swizzle_aos(coord_bld, rho_vec, swizzle2);
+
+               rho = lp_build_max(coord_bld, rho_s, rho_t);
+
+               if (dims >= 3) {
+                  static const unsigned char swizzle3[] = {
+                     2, LP_BLD_SWIZZLE_DONTCARE,
+                     LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
+                  };
+                  rho_r = lp_build_swizzle_aos(coord_bld, rho_vec, swizzle3);
+                  rho = lp_build_max(coord_bld, rho, rho_r);
+               }
             }
          }
-      }
-      rho = lp_build_pack_aos_scalars(bld->gallivm, coord_bld->type,
-                                      perquadf_bld->type, rho);
-   }
-   else {
-      if (dims <= 1) {
-         rho_vec = LLVMBuildExtractElement(builder, rho_vec, index0, "");
-      }
-      rho_vec = lp_build_mul(float_size_bld, rho_vec, float_size);
-
-      if (dims <= 1) {
-         rho = rho_vec;
+         rho = lp_build_pack_aos_scalars(bld->gallivm, coord_bld->type,
+                                         perquadf_bld->type, rho, 0);
       }
       else {
-         if (dims >= 2) {
-            LLVMValueRef rho_s, rho_t, rho_r;
+         if (dims <= 1) {
+            rho_vec = LLVMBuildExtractElement(builder, rho_vec, index0, "");
+         }
+         rho_vec = lp_build_mul(float_size_bld, rho_vec, float_size);
 
-            rho_s = LLVMBuildExtractElement(builder, rho_vec, index0, "");
-            rho_t = LLVMBuildExtractElement(builder, rho_vec, index1, "");
+         if (dims <= 1) {
+            rho = rho_vec;
+         }
+         else {
+            if (dims >= 2) {
+               LLVMValueRef rho_s, rho_t, rho_r;
+
+               rho_s = LLVMBuildExtractElement(builder, rho_vec, index0, "");
+               rho_t = LLVMBuildExtractElement(builder, rho_vec, index1, "");
 
-            rho = lp_build_max(float_bld, rho_s, rho_t);
+               rho = lp_build_max(float_bld, rho_s, rho_t);
 
-            if (dims >= 3) {
-               rho_r = LLVMBuildExtractElement(builder, rho_vec, index2, "");
-               rho = lp_build_max(float_bld, rho, rho_r);
+               if (dims >= 3) {
+                  rho_r = LLVMBuildExtractElement(builder, rho_vec, index2, "");
+                  rho = lp_build_max(float_bld, rho, rho_r);
+               }
             }
          }
       }
@@ -490,7 +571,12 @@ lp_build_brilinear_rho(struct lp_build_context *bld,
  */
 void
 lp_build_lod_selector(struct lp_build_sample_context *bld,
-                      unsigned unit,
+                      unsigned texture_unit,
+                      unsigned sampler_unit,
+                      LLVMValueRef s,
+                      LLVMValueRef t,
+                      LLVMValueRef r,
+                      LLVMValueRef cube_rho,
                       const struct lp_derivatives *derivs,
                       LLVMValueRef lod_bias, /* optional */
                       LLVMValueRef explicit_lod, /* optional */
@@ -506,33 +592,34 @@ lp_build_lod_selector(struct lp_build_sample_context *bld,
    *out_lod_ipart = bld->perquadi_bld.zero;
    *out_lod_fpart = perquadf_bld->zero;
 
-   if (bld->static_state->min_max_lod_equal) {
+   if (bld->static_sampler_state->min_max_lod_equal) {
       /* User is forcing sampling from a particular mipmap level.
        * This is hit during mipmap generation.
        */
       LLVMValueRef min_lod =
-         bld->dynamic_state->min_lod(bld->dynamic_state, bld->gallivm, unit);
+         bld->dynamic_state->min_lod(bld->dynamic_state,
+                                     bld->gallivm, sampler_unit);
 
       lod = lp_build_broadcast_scalar(perquadf_bld, min_lod);
    }
    else {
       if (explicit_lod) {
          lod = lp_build_pack_aos_scalars(bld->gallivm, bld->coord_bld.type,
-                                         perquadf_bld->type, explicit_lod);
+                                         perquadf_bld->type, explicit_lod, 0);
       }
       else {
          LLVMValueRef rho;
 
-         rho = lp_build_rho(bld, unit, derivs);
+         rho = lp_build_rho(bld, texture_unit, s, t, r, cube_rho, derivs);
 
          /*
           * Compute lod = log2(rho)
           */
 
          if (!lod_bias &&
-             !bld->static_state->lod_bias_non_zero &&
-             !bld->static_state->apply_max_lod &&
-             !bld->static_state->apply_min_lod) {
+             !bld->static_sampler_state->lod_bias_non_zero &&
+             !bld->static_sampler_state->apply_max_lod &&
+             !bld->static_sampler_state->apply_min_lod) {
             /*
              * Special case when there are no post-log2 adjustments, which
              * saves instructions but keeping the integer and fractional lod
@@ -563,31 +650,34 @@ lp_build_lod_selector(struct lp_build_sample_context *bld,
          /* add shader lod bias */
          if (lod_bias) {
             lod_bias = lp_build_pack_aos_scalars(bld->gallivm, bld->coord_bld.type,
-                  perquadf_bld->type, lod_bias);
+                  perquadf_bld->type, lod_bias, 0);
             lod = LLVMBuildFAdd(builder, lod, lod_bias, "shader_lod_bias");
          }
       }
 
       /* add sampler lod bias */
-      if (bld->static_state->lod_bias_non_zero) {
+      if (bld->static_sampler_state->lod_bias_non_zero) {
          LLVMValueRef sampler_lod_bias =
-            bld->dynamic_state->lod_bias(bld->dynamic_state, bld->gallivm, unit);
+            bld->dynamic_state->lod_bias(bld->dynamic_state,
+                                         bld->gallivm, sampler_unit);
          sampler_lod_bias = lp_build_broadcast_scalar(perquadf_bld,
                                                       sampler_lod_bias);
          lod = LLVMBuildFAdd(builder, lod, sampler_lod_bias, "sampler_lod_bias");
       }
 
       /* clamp lod */
-      if (bld->static_state->apply_max_lod) {
+      if (bld->static_sampler_state->apply_max_lod) {
          LLVMValueRef max_lod =
-            bld->dynamic_state->max_lod(bld->dynamic_state, bld->gallivm, unit);
+            bld->dynamic_state->max_lod(bld->dynamic_state,
+                                        bld->gallivm, sampler_unit);
          max_lod = lp_build_broadcast_scalar(perquadf_bld, max_lod);
 
          lod = lp_build_min(perquadf_bld, lod, max_lod);
       }
-      if (bld->static_state->apply_min_lod) {
+      if (bld->static_sampler_state->apply_min_lod) {
          LLVMValueRef min_lod =
-            bld->dynamic_state->min_lod(bld->dynamic_state, bld->gallivm, unit);
+            bld->dynamic_state->min_lod(bld->dynamic_state,
+                                        bld->gallivm, sampler_unit);
          min_lod = lp_build_broadcast_scalar(perquadf_bld, min_lod);
 
          lod = lp_build_max(perquadf_bld, lod, min_lod);
@@ -624,7 +714,7 @@ lp_build_lod_selector(struct lp_build_sample_context *bld,
  */
 void
 lp_build_nearest_mip_level(struct lp_build_sample_context *bld,
-                           unsigned unit,
+                           unsigned texture_unit,
                            LLVMValueRef lod_ipart,
                            LLVMValueRef *level_out)
 {
@@ -632,9 +722,9 @@ lp_build_nearest_mip_level(struct lp_build_sample_context *bld,
    LLVMValueRef first_level, last_level, level;
 
    first_level = bld->dynamic_state->first_level(bld->dynamic_state,
-                                                 bld->gallivm, unit);
+                                                 bld->gallivm, texture_unit);
    last_level = bld->dynamic_state->last_level(bld->dynamic_state,
-                                               bld->gallivm, unit);
+                                               bld->gallivm, texture_unit);
    first_level = lp_build_broadcast_scalar(perquadi_bld, first_level);
    last_level = lp_build_broadcast_scalar(perquadi_bld, last_level);
 
@@ -652,7 +742,7 @@ lp_build_nearest_mip_level(struct lp_build_sample_context *bld,
  */
 void
 lp_build_linear_mip_levels(struct lp_build_sample_context *bld,
-                           unsigned unit,
+                           unsigned texture_unit,
                            LLVMValueRef lod_ipart,
                            LLVMValueRef *lod_fpart_inout,
                            LLVMValueRef *level0_out,
@@ -666,9 +756,9 @@ lp_build_linear_mip_levels(struct lp_build_sample_context *bld,
    LLVMValueRef clamp_max;
 
    first_level = bld->dynamic_state->first_level(bld->dynamic_state,
-                                                 bld->gallivm, unit);
+                                                 bld->gallivm, texture_unit);
    last_level = bld->dynamic_state->last_level(bld->dynamic_state,
-                                               bld->gallivm, unit);
+                                               bld->gallivm, texture_unit);
    first_level = lp_build_broadcast_scalar(perquadi_bld, first_level);
    last_level = lp_build_broadcast_scalar(perquadi_bld, last_level);
 
@@ -718,15 +808,14 @@ lp_build_linear_mip_levels(struct lp_build_sample_context *bld,
    *lod_fpart_inout = LLVMBuildSelect(builder, clamp_max,
                                       perquadf_bld->zero, *lod_fpart_inout, "");
 
-   lp_build_name(*level0_out, "sampler%u_miplevel0", unit);
-   lp_build_name(*level1_out, "sampler%u_miplevel1", unit);
-   lp_build_name(*lod_fpart_inout, "sampler%u_mipweight", unit);
+   lp_build_name(*level0_out, "texture%u_miplevel0", texture_unit);
+   lp_build_name(*level1_out, "texture%u_miplevel1", texture_unit);
+   lp_build_name(*lod_fpart_inout, "texture%u_mipweight", texture_unit);
 }
 
 
 /**
  * Return pointer to a single mipmap level.
- * \param data_array  array of pointers to mipmap levels
  * \param level  integer mipmap level
  */
 LLVMValueRef
@@ -734,15 +823,65 @@ lp_build_get_mipmap_level(struct lp_build_sample_context *bld,
                           LLVMValueRef level)
 {
    LLVMBuilderRef builder = bld->gallivm->builder;
-   LLVMValueRef indexes[2], data_ptr;
+   LLVMValueRef indexes[2], data_ptr, mip_offset;
 
    indexes[0] = lp_build_const_int32(bld->gallivm, 0);
    indexes[1] = level;
-   data_ptr = LLVMBuildGEP(builder, bld->data_array, indexes, 2, "");
-   data_ptr = LLVMBuildLoad(builder, data_ptr, "");
+   mip_offset = LLVMBuildGEP(builder, bld->mip_offsets, indexes, 2, "");
+   mip_offset = LLVMBuildLoad(builder, mip_offset, "");
+   data_ptr = LLVMBuildGEP(builder, bld->base_ptr, &mip_offset, 1, "");
    return data_ptr;
 }
 
+/**
+ * Return (per-pixel) offsets to mip levels.
+ * \param level  integer mipmap level
+ */
+LLVMValueRef
+lp_build_get_mip_offsets(struct lp_build_sample_context *bld,
+                         LLVMValueRef level)
+{
+   LLVMBuilderRef builder = bld->gallivm->builder;
+   LLVMValueRef indexes[2], offsets, offset1;
+
+   indexes[0] = lp_build_const_int32(bld->gallivm, 0);
+   if (bld->num_lods == 1) {
+      indexes[1] = level;
+      offset1 = LLVMBuildGEP(builder, bld->mip_offsets, indexes, 2, "");
+      offset1 = LLVMBuildLoad(builder, offset1, "");
+      offsets = lp_build_broadcast_scalar(&bld->int_coord_bld, offset1);
+   }
+   else if (bld->num_lods == bld->coord_bld.type.length / 4) {
+      unsigned i;
+
+      offsets = bld->int_coord_bld.undef;
+      for (i = 0; i < bld->num_lods; i++) {
+         LLVMValueRef indexi = lp_build_const_int32(bld->gallivm, i);
+         LLVMValueRef indexo = lp_build_const_int32(bld->gallivm, 4 * i);
+         indexes[1] = LLVMBuildExtractElement(builder, level, indexi, "");
+         offset1 = LLVMBuildGEP(builder, bld->mip_offsets, indexes, 2, "");
+         offset1 = LLVMBuildLoad(builder, offset1, "");
+         offsets = LLVMBuildInsertElement(builder, offsets, offset1, indexo, "");
+      }
+      offsets = lp_build_swizzle_scalar_aos(&bld->int_coord_bld, offsets, 0, 4);
+   }
+   else {
+      unsigned i;
+
+      assert (bld->num_lods == bld->coord_bld.type.length);
+
+      offsets = bld->int_coord_bld.undef;
+      for (i = 0; i < bld->num_lods; i++) {
+         LLVMValueRef indexi = lp_build_const_int32(bld->gallivm, i);
+         indexes[1] = LLVMBuildExtractElement(builder, level, indexi, "");
+         offset1 = LLVMBuildGEP(builder, bld->mip_offsets, indexes, 2, "");
+         offset1 = LLVMBuildLoad(builder, offset1, "");
+         offsets = LLVMBuildInsertElement(builder, offsets, offset1, indexi, "");
+      }
+   }
+   return offsets;
+}
+
 
 /**
  * Codegen equivalent for u_minify().
@@ -780,12 +919,44 @@ lp_build_get_level_stride_vec(struct lp_build_sample_context *bld,
                               LLVMValueRef stride_array, LLVMValueRef level)
 {
    LLVMBuilderRef builder = bld->gallivm->builder;
-   LLVMValueRef indexes[2], stride;
+   LLVMValueRef indexes[2], stride, stride1;
    indexes[0] = lp_build_const_int32(bld->gallivm, 0);
-   indexes[1] = level;
-   stride = LLVMBuildGEP(builder, stride_array, indexes, 2, "");
-   stride = LLVMBuildLoad(builder, stride, "");
-   stride = lp_build_broadcast_scalar(&bld->int_coord_bld, stride);
+   if (bld->num_lods == 1) {
+      indexes[1] = level;
+      stride1 = LLVMBuildGEP(builder, stride_array, indexes, 2, "");
+      stride1 = LLVMBuildLoad(builder, stride1, "");
+      stride = lp_build_broadcast_scalar(&bld->int_coord_bld, stride1);
+   }
+   else if (bld->num_lods == bld->coord_bld.type.length / 4) {
+      LLVMValueRef stride1;
+      unsigned i;
+
+      stride = bld->int_coord_bld.undef;
+      for (i = 0; i < bld->num_lods; i++) {
+         LLVMValueRef indexi = lp_build_const_int32(bld->gallivm, i);
+         LLVMValueRef indexo = lp_build_const_int32(bld->gallivm, i);
+         indexes[1] = LLVMBuildExtractElement(builder, level, indexi, "");
+         stride1 = LLVMBuildGEP(builder, stride_array, indexes, 2, "");
+         stride1 = LLVMBuildLoad(builder, stride1, "");
+         stride = LLVMBuildInsertElement(builder, stride, stride1, indexo, "");
+      }
+      stride = lp_build_swizzle_scalar_aos(&bld->int_coord_bld, stride, 0, 4);
+   }
+   else {
+      LLVMValueRef stride1;
+      unsigned i;
+
+      assert (bld->num_lods == bld->coord_bld.type.length);
+
+      stride = bld->int_coord_bld.undef;
+      for (i = 0; i < bld->coord_bld.type.length; i++) {
+         LLVMValueRef indexi = lp_build_const_int32(bld->gallivm, i);
+         indexes[1] = LLVMBuildExtractElement(builder, level, indexi, "");
+         stride1 = LLVMBuildGEP(builder, stride_array, indexes, 2, "");
+         stride1 = LLVMBuildLoad(builder, stride1, "");
+         stride = LLVMBuildInsertElement(builder, stride, stride1, indexi, "");
+      }
+   }
    return stride;
 }
 
@@ -805,22 +976,115 @@ lp_build_mipmap_level_sizes(struct lp_build_sample_context *bld,
    const unsigned dims = bld->dims;
    LLVMValueRef ilevel_vec;
 
-   ilevel_vec = lp_build_broadcast_scalar(&bld->int_size_bld, ilevel);
-
    /*
     * Compute width, height, depth at mipmap level 'ilevel'
     */
-   *out_size = lp_build_minify(&bld->int_size_bld, bld->int_size, ilevel_vec);
+   if (bld->num_lods == 1) {
+      ilevel_vec = lp_build_broadcast_scalar(&bld->int_size_bld, ilevel);
+      *out_size = lp_build_minify(&bld->int_size_bld, bld->int_size, ilevel_vec);
+   }
+   else {
+      LLVMValueRef int_size_vec;
+      LLVMValueRef tmp[LP_MAX_VECTOR_LENGTH];
+      unsigned num_quads = bld->coord_bld.type.length / 4;
+      unsigned i;
+
+      if (bld->num_lods == num_quads) {
+         /*
+          * XXX: this should be #ifndef SANE_INSTRUCTION_SET.
+          * intel "forgot" the variable shift count instruction until avx2.
+          * A harmless 8x32 shift gets translated into 32 instructions
+          * (16 extracts, 8 scalar shifts, 8 inserts), llvm is apparently
+          * unable to recognize if there are really just 2 different shift
+          * count values. So do the shift 4-wide before expansion.
+          */
+         struct lp_build_context bld4;
+         struct lp_type type4;
+
+         type4 = bld->int_coord_bld.type;
+         type4.length = 4;
+
+         lp_build_context_init(&bld4, bld->gallivm, type4);
+
+         if (bld->dims == 1) {
+            assert(bld->int_size_in_bld.type.length == 1);
+            int_size_vec = lp_build_broadcast_scalar(&bld4,
+                                                     bld->int_size);
+         }
+         else {
+            assert(bld->int_size_in_bld.type.length == 4);
+            int_size_vec = bld->int_size;
+         }
+
+         for (i = 0; i < num_quads; i++) {
+            LLVMValueRef ileveli;
+            LLVMValueRef indexi = lp_build_const_int32(bld->gallivm, i);
+
+            ileveli = lp_build_extract_broadcast(bld->gallivm,
+                                                 bld->perquadi_bld.type,
+                                                 bld4.type,
+                                                 ilevel,
+                                                 indexi);
+            tmp[i] = lp_build_minify(&bld4, int_size_vec, ileveli);
+         }
+         /*
+          * out_size is [w0, h0, d0, _, w1, h1, d1, _, ...] vector for dims > 1,
+          * [w0, w0, w0, w0, w1, w1, w1, w1, ...] otherwise.
+          */
+         *out_size = lp_build_concat(bld->gallivm,
+                                     tmp,
+                                     bld4.type,
+                                     num_quads);
+      }
+      else {
+        /* FIXME: this is terrible and results in _huge_ vector
+         * (for the dims > 1 case).
+         * Should refactor this (together with extract_image_sizes) and do
+         * something more useful. Could for instance if we have width,height
+         * with 4-wide vector pack all elements into a 8xi16 vector
+         * (on which we can still do useful math) instead of using a 16xi32
+         * vector.
+         * FIXME: some callers can't handle this yet.
+         * For dims == 1 this will create [w0, w1, w2, w3, ...] vector.
+         * For dims > 1 this will create [w0, h0, d0, _, w1, h1, d1, _, ...] vector.
+         */
+         assert(bld->num_lods == bld->coord_bld.type.length);
+         if (bld->dims == 1) {
+            assert(bld->int_size_bld.type.length == 1);
+            int_size_vec = lp_build_broadcast_scalar(&bld->int_coord_bld,
+                                                     bld->int_size);
+            /* vector shift with variable shift count alert... */
+            *out_size = lp_build_minify(&bld->int_coord_bld, int_size_vec, ilevel);
+         }
+         else {
+            LLVMValueRef ilevel1;
+            for (i = 0; i < bld->num_lods; i++) {
+               LLVMValueRef indexi = lp_build_const_int32(bld->gallivm, i);
+               ilevel1 = lp_build_extract_broadcast(bld->gallivm, bld->int_coord_type,
+                                                    bld->int_size_in_bld.type, ilevel, indexi);
+               tmp[i] = bld->int_size;
+               tmp[i] = lp_build_minify(&bld->int_size_in_bld, tmp[i], ilevel1);
+            }
+            int_size_vec = lp_build_concat(bld->gallivm,
+                                           tmp,
+                                           bld->int_size_in_bld.type,
+                                           bld->num_lods);
+         }
+      }
+   }
 
    if (dims >= 2) {
       *row_stride_vec = lp_build_get_level_stride_vec(bld,
                                                       bld->row_stride_array,
                                                       ilevel);
-      if (dims == 3 || bld->static_state->target == PIPE_TEXTURE_CUBE) {
-         *img_stride_vec = lp_build_get_level_stride_vec(bld,
-                                                         bld->img_stride_array,
-                                                         ilevel);
-      }
+   }
+   if (dims == 3 ||
+       bld->static_texture_state->target == PIPE_TEXTURE_CUBE ||
+       bld->static_texture_state->target == PIPE_TEXTURE_1D_ARRAY ||
+       bld->static_texture_state->target == PIPE_TEXTURE_2D_ARRAY) {
+      *img_stride_vec = lp_build_get_level_stride_vec(bld,
+                                                      bld->img_stride_array,
+                                                      ilevel);
    }
 }
 
@@ -836,7 +1100,7 @@ lp_build_mipmap_level_sizes(struct lp_build_sample_context *bld,
  */
 void
 lp_build_extract_image_sizes(struct lp_build_sample_context *bld,
-                             struct lp_type size_type,
+                             struct lp_build_context *size_bld,
                              struct lp_type coord_type,
                              LLVMValueRef size,
                              LLVMValueRef *out_width,
@@ -845,24 +1109,56 @@ lp_build_extract_image_sizes(struct lp_build_sample_context *bld,
 {
    const unsigned dims = bld->dims;
    LLVMTypeRef i32t = LLVMInt32TypeInContext(bld->gallivm->context);
+   struct lp_type size_type = size_bld->type;
+
+   if (bld->num_lods == 1) {
+      *out_width = lp_build_extract_broadcast(bld->gallivm,
+                                              size_type,
+                                              coord_type,
+                                              size,
+                                              LLVMConstInt(i32t, 0, 0));
+      if (dims >= 2) {
+         *out_height = lp_build_extract_broadcast(bld->gallivm,
+                                                  size_type,
+                                                  coord_type,
+                                                  size,
+                                                  LLVMConstInt(i32t, 1, 0));
+         if (dims == 3) {
+            *out_depth = lp_build_extract_broadcast(bld->gallivm,
+                                                    size_type,
+                                                    coord_type,
+                                                    size,
+                                                    LLVMConstInt(i32t, 2, 0));
+         }
+      }
+   }
+   else {
+      unsigned num_quads = bld->coord_bld.type.length / 4;
 
-   *out_width = lp_build_extract_broadcast(bld->gallivm,
-                                           size_type,
-                                           coord_type,
-                                           size,
-                                           LLVMConstInt(i32t, 0, 0));
-   if (dims >= 2) {
-      *out_height = lp_build_extract_broadcast(bld->gallivm,
-                                               size_type,
-                                               coord_type,
-                                               size,
-                                               LLVMConstInt(i32t, 1, 0));
-      if (dims == 3) {
-         *out_depth = lp_build_extract_broadcast(bld->gallivm,
-                                                 size_type,
-                                                 coord_type,
-                                                 size,
-                                                 LLVMConstInt(i32t, 2, 0));
+      if (dims == 1) {
+         *out_width = size;
+      }
+      else if (bld->num_lods == num_quads) {
+         *out_width = lp_build_swizzle_scalar_aos(size_bld, size, 0, 4);
+         if (dims >= 2) {
+            *out_height = lp_build_swizzle_scalar_aos(size_bld, size, 1, 4);
+            if (dims == 3) {
+               *out_depth = lp_build_swizzle_scalar_aos(size_bld, size, 2, 4);
+            }
+         }
+      }
+      else {
+         assert(bld->num_lods == bld->coord_type.length);
+         *out_width = lp_build_pack_aos_scalars(bld->gallivm, size_type,
+                                                coord_type, size, 0);
+         if (dims >= 2) {
+            *out_width = lp_build_pack_aos_scalars(bld->gallivm, size_type,
+                                                   coord_type, size, 1);
+            if (dims == 3) {
+               *out_width = lp_build_pack_aos_scalars(bld->gallivm, size_type,
+                                                      coord_type, size, 2);
+            }
+         }
       }
    }
 }
@@ -886,7 +1182,7 @@ lp_build_unnormalized_coords(struct lp_build_sample_context *bld,
    LLVMValueRef depth;
 
    lp_build_extract_image_sizes(bld,
-                                bld->float_size_type,
+                                &bld->float_size_bld,
                                 bld->coord_type,
                                 flt_size,
                                 &width,
@@ -993,33 +1289,36 @@ lp_build_cube_lookup(struct lp_build_sample_context *bld,
                      LLVMValueRef r,
                      LLVMValueRef *face,
                      LLVMValueRef *face_s,
-                     LLVMValueRef *face_t)
+                     LLVMValueRef *face_t,
+                     LLVMValueRef *rho)
 {
    struct lp_build_context *coord_bld = &bld->coord_bld;
    LLVMBuilderRef builder = bld->gallivm->builder;
    struct gallivm_state *gallivm = bld->gallivm;
-   LLVMValueRef rx, ry, rz;
-   LLVMValueRef tmp[4], rxyz, arxyz;
+   LLVMValueRef si, ti, ri;
+   boolean implicit_derivs = TRUE;
+   boolean need_derivs = TRUE;
 
-   /*
-    * Use the average of the four pixel's texcoords to choose the face.
-    * Slight simplification just calculate the sum, skip scaling.
-    */
-   tmp[0] = s;
-   tmp[1] = t;
-   tmp[2] = r;
-   rxyz = lp_build_hadd_partial4(&bld->coord_bld, tmp, 3);
-   arxyz = lp_build_abs(&bld->coord_bld, rxyz);
-
-   if (coord_bld->type.length > 4) {
+   if (1 || coord_bld->type.length > 4) {
+      /*
+       * Do per-pixel face selection. We cannot however (as we used to do)
+       * simply calculate the derivs afterwards (which is very bogus for
+       * explicit derivs anyway) because the values would be "random" when
+       * not all pixels lie on the same face. Hence just transform the derivs
+       * (or rather only the dmax values), which works both for implicit and
+       * explicit derivatives and doesn't add much math (except need to
+       * calculate derivs for 3 instead of 2 coords and have a couple more selects
+       * but cuts some minor math elsewhere). The derivs don't need mirroring,
+       * just selection, since noone cares about the sign.
+       */
       struct lp_build_context *cint_bld = &bld->int_coord_bld;
       struct lp_type intctype = cint_bld->type;
-      LLVMValueRef signrxs, signrys, signrzs, signrxyz, sign;
-      LLVMValueRef arxs, arys, arzs;
-      LLVMValueRef arx_ge_ary, maxarxsarys, arz_ge_arx_ary;
+      LLVMValueRef signs, signt, signr, signma;
+      LLVMValueRef as, at, ar;
+      LLVMValueRef as_ge_at, maxasat, ar_ge_as_at;
       LLVMValueRef snewx, tnewx, snewy, tnewy, snewz, tnewz;
-      LLVMValueRef ryneg, rzneg;
-      LLVMValueRef ma, ima;
+      LLVMValueRef tnegi, rnegi;
+      LLVMValueRef ma, mai, ima;
       LLVMValueRef posHalf = lp_build_const_vec(gallivm, coord_bld->type, 0.5);
       LLVMValueRef signmask = lp_build_const_int_vec(gallivm, intctype,
                                                      1 << (intctype.width - 1));
@@ -1028,54 +1327,102 @@ lp_build_cube_lookup(struct lp_build_sample_context *bld,
       LLVMValueRef facex = lp_build_const_int_vec(gallivm, intctype, PIPE_TEX_FACE_POS_X);
       LLVMValueRef facey = lp_build_const_int_vec(gallivm, intctype, PIPE_TEX_FACE_POS_Y);
       LLVMValueRef facez = lp_build_const_int_vec(gallivm, intctype, PIPE_TEX_FACE_POS_Z);
+      LLVMValueRef dmax[3], dmaxsnew, dmaxtnew;
 
       assert(PIPE_TEX_FACE_NEG_X == PIPE_TEX_FACE_POS_X + 1);
       assert(PIPE_TEX_FACE_NEG_Y == PIPE_TEX_FACE_POS_Y + 1);
       assert(PIPE_TEX_FACE_NEG_Z == PIPE_TEX_FACE_POS_Z + 1);
 
-      rx = LLVMBuildBitCast(builder, s, lp_build_vec_type(gallivm, intctype), "");
-      ry = LLVMBuildBitCast(builder, t, lp_build_vec_type(gallivm, intctype), "");
-      rz = LLVMBuildBitCast(builder, r, lp_build_vec_type(gallivm, intctype), "");
-      ryneg = LLVMBuildXor(builder, ry, signmask, "");
-      rzneg = LLVMBuildXor(builder, rz, signmask, "");
+      /*
+       * TODO do this only when needed, and implement explicit derivs (trivial).
+       */
+      if (need_derivs && implicit_derivs) {
+         LLVMValueRef ddx_ddy[2], tmp[2];
+         /*
+          * This isn't quite the same as the "ordinary" path since
+          * we need to extract the ds/dt/dr values before further processing.
+          */
+         static const unsigned char swizzle11[] = { /* no-op swizzle */
+            0, LP_BLD_SWIZZLE_DONTCARE,
+            LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
+         };
+         static const unsigned char swizzle12[] = {
+            2, LP_BLD_SWIZZLE_DONTCARE,
+            LP_BLD_SWIZZLE_DONTCARE, LP_BLD_SWIZZLE_DONTCARE
+         };
+         static const unsigned char swizzle21[] = { /* no-op swizzle */
+            0, LP_BLD_SWIZZLE_DONTCARE,
+            2, LP_BLD_SWIZZLE_DONTCARE
+         };
+         static const unsigned char swizzle22[] = {
+            1, LP_BLD_SWIZZLE_DONTCARE,
+            3, LP_BLD_SWIZZLE_DONTCARE
+         };
+
+         ddx_ddy[0] = lp_build_packed_ddx_ddy_twocoord(coord_bld, s, t);
+         ddx_ddy[1] = lp_build_packed_ddx_ddy_onecoord(coord_bld, r);
+         ddx_ddy[0] = lp_build_abs(coord_bld, ddx_ddy[0]);
+         ddx_ddy[1] = lp_build_abs(coord_bld, ddx_ddy[1]);
+
+         tmp[0] = lp_build_swizzle_aos(coord_bld, ddx_ddy[0], swizzle21);
+         tmp[1] = lp_build_swizzle_aos(coord_bld, ddx_ddy[0], swizzle22);
+         dmax[0] = lp_build_max(coord_bld, tmp[0], tmp[1]);
+         dmax[1] = lp_build_swizzle_aos(coord_bld, dmax[0], swizzle12);
+
+         tmp[0] = lp_build_swizzle_aos(coord_bld, ddx_ddy[1], swizzle11);
+         tmp[1] = lp_build_swizzle_aos(coord_bld, ddx_ddy[1], swizzle12);
+         dmax[2] = lp_build_max(coord_bld, tmp[0], tmp[1]);
+      }
+      else if (need_derivs) {
+         /* dmax[0] = lp_build_max(coord_bld, derivs->ddx[0], derivs->ddy[0]);
+         dmax[1] = lp_build_max(coord_bld, derivs->ddx[1], derivs->ddy[1]);
+         dmax[2] = lp_build_max(coord_bld, derivs->ddx[2], derivs->ddy[2]); */
+      }
 
-      /* the sign bit comes from the averaged vector (per quad),
-       * as does the decision which face to use */
-      signrxyz = LLVMBuildBitCast(builder, rxyz, lp_build_vec_type(gallivm, intctype), "");
-      signrxyz = LLVMBuildAnd(builder, signrxyz, signmask, "");
+      si = LLVMBuildBitCast(builder, s, lp_build_vec_type(gallivm, intctype), "");
+      ti = LLVMBuildBitCast(builder, t, lp_build_vec_type(gallivm, intctype), "");
+      ri = LLVMBuildBitCast(builder, r, lp_build_vec_type(gallivm, intctype), "");
 
-      arxs = lp_build_swizzle_scalar_aos(coord_bld, arxyz, 0);
-      arys = lp_build_swizzle_scalar_aos(coord_bld, arxyz, 1);
-      arzs = lp_build_swizzle_scalar_aos(coord_bld, arxyz, 2);
+      /*
+       * get absolute value (for x/y/z face selection) and sign bit
+       * (for mirroring minor coords and pos/neg face selection)
+       * of the original coords.
+       */
+      as = lp_build_abs(&bld->coord_bld, s);
+      at = lp_build_abs(&bld->coord_bld, t);
+      ar = lp_build_abs(&bld->coord_bld, r);
+      signs = LLVMBuildAnd(builder, si, signmask, "");
+      signt = LLVMBuildAnd(builder, ti, signmask, "");
+      signr = LLVMBuildAnd(builder, ri, signmask, "");
 
       /*
-       * select x if x >= y else select y
+       * major face determination: select x if x >= y else select y
        * select previous result if y >= max(x,y) else select z
        */
-      arx_ge_ary = lp_build_cmp(coord_bld, PIPE_FUNC_GEQUAL, arxs, arys);
-      maxarxsarys = lp_build_max(coord_bld, arxs, arys);
-      arz_ge_arx_ary = lp_build_cmp(coord_bld, PIPE_FUNC_GEQUAL, maxarxsarys, arzs);
+      as_ge_at = lp_build_cmp(coord_bld, PIPE_FUNC_GEQUAL, as, at);
+      maxasat = lp_build_max(coord_bld, as, at);
+      ar_ge_as_at = lp_build_cmp(coord_bld, PIPE_FUNC_GEQUAL, maxasat, ar);
 
       /*
        * compute all possible new s/t coords
-       * snewx = signrx * -rz;
-       * tnewx = -ry;
-       * snewy = rx;
-       * tnewy = signry * rz;
-       * snewz = signrz * rx;
-       * tnewz = -ry;
+       * snewx = signs * -r;
+       * tnewx = -t;
+       * snewy = s;
+       * tnewy = signt * r;
+       * snewz = signr * s;
+       * tnewz = -t;
        */
-      signrxs = lp_build_swizzle_scalar_aos(cint_bld, signrxyz, 0);
-      snewx = LLVMBuildXor(builder, signrxs, rzneg, "");
-      tnewx = ryneg;
+      tnegi = LLVMBuildXor(builder, ti, signmask, "");
+      rnegi = LLVMBuildXor(builder, ri, signmask, "");
+
+      snewx = LLVMBuildXor(builder, signs, rnegi, "");
+      tnewx = tnegi;
 
-      signrys = lp_build_swizzle_scalar_aos(cint_bld, signrxyz, 1);
-      snewy = rx;
-      tnewy = LLVMBuildXor(builder, signrys, rz, "");
+      snewy = si;
+      tnewy = LLVMBuildXor(builder, signt, ri, "");
 
-      signrzs = lp_build_swizzle_scalar_aos(cint_bld, signrxyz, 2);
-      snewz = LLVMBuildXor(builder, signrzs, rx, "");
-      tnewz = ryneg;
+      snewz = LLVMBuildXor(builder, signr, si, "");
+      tnewz = tnegi;
 
       /* XXX on x86 unclear if we should cast the values back to float
        * or not - on some cpus (nehalem) pblendvb has twice the throughput
@@ -1083,20 +1430,26 @@ lp_build_cube_lookup(struct lp_build_sample_context *bld,
        * transition penalties when using it (this depends on what llvm
        * will chose for the bit ops above so there appears no "right way",
        * but given the boatload of selects let's just use the int type).
-       *
-       * Unfortunately we also need the sign bit of the summed coords.
        */
-      *face_s = lp_build_select(cint_bld, arx_ge_ary, snewx, snewy);
-      *face_t = lp_build_select(cint_bld, arx_ge_ary, tnewx, tnewy);
-      ma = lp_build_select(coord_bld, arx_ge_ary, s, t);
-      *face = lp_build_select(cint_bld, arx_ge_ary, facex, facey);
-      sign = lp_build_select(cint_bld, arx_ge_ary, signrxs, signrys);
-
-      *face_s = lp_build_select(cint_bld, arz_ge_arx_ary, *face_s, snewz);
-      *face_t = lp_build_select(cint_bld, arz_ge_arx_ary, *face_t, tnewz);
-      ma = lp_build_select(coord_bld, arz_ge_arx_ary, ma, r);
-      *face = lp_build_select(cint_bld, arz_ge_arx_ary, *face, facez);
-      sign = lp_build_select(cint_bld, arz_ge_arx_ary, sign, signrzs);
+
+      /* select/mirror */
+      *face_s = lp_build_select(cint_bld, as_ge_at, snewx, snewy);
+      *face_t = lp_build_select(cint_bld, as_ge_at, tnewx, tnewy);
+      ma = lp_build_select(coord_bld, as_ge_at, s, t);
+      *face = lp_build_select(cint_bld, as_ge_at, facex, facey);
+      if (need_derivs) {
+         dmaxsnew = lp_build_select(coord_bld, as_ge_at, dmax[2], dmax[0]);
+         dmaxtnew = lp_build_select(coord_bld, as_ge_at, dmax[1], dmax[2]);
+      }
+
+      *face_s = lp_build_select(cint_bld, ar_ge_as_at, *face_s, snewz);
+      *face_t = lp_build_select(cint_bld, ar_ge_as_at, *face_t, tnewz);
+      ma = lp_build_select(coord_bld, ar_ge_as_at, ma, r);
+      *face = lp_build_select(cint_bld, ar_ge_as_at, *face, facez);
+      if (need_derivs) {
+         dmaxsnew = lp_build_select(coord_bld, ar_ge_as_at, dmaxsnew, dmax[0]);
+         dmaxtnew = lp_build_select(coord_bld, ar_ge_as_at, dmaxtnew, dmax[1]);
+      }
 
       *face_s = LLVMBuildBitCast(builder, *face_s,
                                lp_build_vec_type(gallivm, coord_bld->type), "");
@@ -1108,15 +1461,30 @@ lp_build_cube_lookup(struct lp_build_sample_context *bld,
        * as long as we ensure vblendvps gets used we can actually
        * skip the comparison and just use sign as a "mask" directly.
        */
-      sign = LLVMBuildLShr(builder, sign, signshift, "");
-      *face = LLVMBuildOr(builder, *face, sign, "face");
+      mai = LLVMBuildBitCast(builder, ma, lp_build_vec_type(gallivm, intctype), "");
+      signma = LLVMBuildLShr(builder, mai, signshift, "");
+      *face = LLVMBuildOr(builder, *face, signma, "face");
 
       ima = lp_build_cube_imapos(coord_bld, ma);
 
+      /* project coords */
       *face_s = lp_build_mul(coord_bld, *face_s, ima);
       *face_s = lp_build_add(coord_bld, *face_s, posHalf);
       *face_t = lp_build_mul(coord_bld, *face_t, ima);
       *face_t = lp_build_add(coord_bld, *face_t, posHalf);
+
+      /* project derivs */
+      if (need_derivs) {
+         /*
+          * we do some optimization here, since we know it's square
+          * we can do the max before projection (and before size mul,
+          * which the so-called "rho" is missing here).
+          * For explicit derivs this is fully per-pixel vector, for implicit
+          * derivs only the first value per quad contains useful values.
+          */
+         *rho = lp_build_max(coord_bld, dmaxsnew, dmaxtnew);
+         *rho = lp_build_mul(coord_bld, *rho, ima);
+      }
    }
 
    else {
@@ -1128,10 +1496,17 @@ lp_build_cube_lookup(struct lp_build_sample_context *bld,
       LLVMValueRef shuffles[4];
       LLVMValueRef arxy_ge_aryx, arxy_ge_arzz, arxy_ge_arxy_arzz;
       LLVMValueRef arxyxy, aryxzz, arxyxy_ge_aryxzz;
+      LLVMValueRef tmp[4], rxyz, arxyz;
       struct lp_build_context *float_bld = &bld->float_bld;
 
       assert(bld->coord_bld.type.length == 4);
 
+      tmp[0] = s;
+      tmp[1] = t;
+      tmp[2] = r;
+      rxyz = lp_build_hadd_partial4(&bld->coord_bld, tmp, 3);
+      arxyz = lp_build_abs(&bld->coord_bld, rxyz);
+
       shuffles[0] = lp_build_const_int32(gallivm, 0);
       shuffles[1] = lp_build_const_int32(gallivm, 1);
       shuffles[2] = lp_build_const_int32(gallivm, 0);
@@ -1170,14 +1545,14 @@ lp_build_cube_lookup(struct lp_build_sample_context *bld,
       {
          /* +/- X face */
          LLVMValueRef sign, ima;
-         rx = LLVMBuildExtractElement(builder, rxyz,
+         si = LLVMBuildExtractElement(builder, rxyz,
                                       lp_build_const_int32(gallivm, 0), "");
          /* +/- X face */
-         sign = lp_build_sgn(float_bld, rx);
+         sign = lp_build_sgn(float_bld, si);
          ima = lp_build_cube_imaneg(coord_bld, s);
          *face_s = lp_build_cube_coord(coord_bld, sign, +1, r, ima);
          *face_t = lp_build_cube_coord(coord_bld, NULL, +1, t, ima);
-         *face = lp_build_cube_face(bld, rx,
+         *face = lp_build_cube_face(bld, si,
                                     PIPE_TEX_FACE_POS_X,
                                     PIPE_TEX_FACE_NEG_X);
          LLVMBuildStore(builder, *face_s, face_s_var);
@@ -1192,13 +1567,13 @@ lp_build_cube_lookup(struct lp_build_sample_context *bld,
          {
             LLVMValueRef sign, ima;
             /* +/- Y face */
-            ry = LLVMBuildExtractElement(builder, rxyz,
+            ti = LLVMBuildExtractElement(builder, rxyz,
                                          lp_build_const_int32(gallivm, 1), "");
-            sign = lp_build_sgn(float_bld, ry);
+            sign = lp_build_sgn(float_bld, ti);
             ima = lp_build_cube_imaneg(coord_bld, t);
             *face_s = lp_build_cube_coord(coord_bld, NULL, -1, s, ima);
             *face_t = lp_build_cube_coord(coord_bld, sign, -1, r, ima);
-            *face = lp_build_cube_face(bld, ry,
+            *face = lp_build_cube_face(bld, ti,
                                        PIPE_TEX_FACE_POS_Y,
                                        PIPE_TEX_FACE_NEG_Y);
             LLVMBuildStore(builder, *face_s, face_s_var);
@@ -1209,13 +1584,13 @@ lp_build_cube_lookup(struct lp_build_sample_context *bld,
          {
             /* +/- Z face */
             LLVMValueRef sign, ima;
-            rz = LLVMBuildExtractElement(builder, rxyz,
+            ri = LLVMBuildExtractElement(builder, rxyz,
                                          lp_build_const_int32(gallivm, 2), "");
-            sign = lp_build_sgn(float_bld, rz);
+            sign = lp_build_sgn(float_bld, ri);
             ima = lp_build_cube_imaneg(coord_bld, r);
             *face_s = lp_build_cube_coord(coord_bld, sign, -1, s, ima);
             *face_t = lp_build_cube_coord(coord_bld, NULL, +1, t, ima);
-            *face = lp_build_cube_face(bld, rz,
+            *face = lp_build_cube_face(bld, ri,
                                        PIPE_TEX_FACE_POS_Z,
                                        PIPE_TEX_FACE_NEG_Z);
             LLVMBuildStore(builder, *face_s, face_s_var);