llvmpipe: fix race between draw and setting fragment shader.
[mesa.git] / src / gallium / drivers / llvmpipe / lp_bld_blend_aos.c
index 66df662711ce8733b4d7d40e1a6f884208a00668..985cda699b3d7be5774599dc30c3fb2b0eb025cd 100644 (file)
@@ -45,7 +45,7 @@
 
 #include "pipe/p_state.h"
 #include "util/u_debug.h"
-#include "util/u_format.h"
+#include "util/format/u_format.h"
 
 #include "gallivm/lp_bld_type.h"
 #include "gallivm/lp_bld_const.h"
 struct lp_build_blend_aos_context
 {
    struct lp_build_context base;
-   
+
    LLVMValueRef src;
+   LLVMValueRef src_alpha;
+   LLVMValueRef src1;
+   LLVMValueRef src1_alpha;
    LLVMValueRef dst;
    LLVMValueRef const_;
+   LLVMValueRef const_alpha;
+   boolean has_dst_alpha;
 
    LLVMValueRef inv_src;
+   LLVMValueRef inv_src_alpha;
    LLVMValueRef inv_dst;
    LLVMValueRef inv_const;
+   LLVMValueRef inv_const_alpha;
    LLVMValueRef saturate;
 
    LLVMValueRef rgb_src_factor;
@@ -88,55 +95,87 @@ lp_build_blend_factor_unswizzled(struct lp_build_blend_aos_context *bld,
                                  unsigned factor,
                                  boolean alpha)
 {
+   LLVMValueRef src_alpha = bld->src_alpha ? bld->src_alpha : bld->src;
+   LLVMValueRef src1_alpha = bld->src1_alpha ? bld->src1_alpha : bld->src1;
+   LLVMValueRef const_alpha = bld->const_alpha ? bld->const_alpha : bld->const_;
+
    switch (factor) {
    case PIPE_BLENDFACTOR_ZERO:
       return bld->base.zero;
    case PIPE_BLENDFACTOR_ONE:
       return bld->base.one;
    case PIPE_BLENDFACTOR_SRC_COLOR:
-   case PIPE_BLENDFACTOR_SRC_ALPHA:
       return bld->src;
+   case PIPE_BLENDFACTOR_SRC_ALPHA:
+      return src_alpha;
    case PIPE_BLENDFACTOR_DST_COLOR:
    case PIPE_BLENDFACTOR_DST_ALPHA:
       return bld->dst;
    case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE:
-      if(alpha)
+      if (alpha)
          return bld->base.one;
       else {
-         if(!bld->inv_dst)
-            bld->inv_dst = lp_build_comp(&bld->base, bld->dst);
-         if(!bld->saturate)
-            bld->saturate = lp_build_min(&bld->base, bld->src, bld->inv_dst);
+         /*
+          * If there's no dst alpha the complement is zero but for unclamped
+          * float inputs (or snorm inputs) min can be non-zero (negative).
+          */
+         if (!bld->saturate) {
+            if (!bld->has_dst_alpha) {
+               bld->saturate = lp_build_min(&bld->base, src_alpha, bld->base.zero);
+            }
+            else if (bld->base.type.norm && bld->base.type.sign) {
+               /*
+                * The complement/min totally doesn't work, since
+                * the complement is in range [0,2] but the other
+                * min input is [-1,1]. However, we can just clamp to 0
+                * before doing the complement...
+                */
+               LLVMValueRef inv_dst;
+               inv_dst = lp_build_max(&bld->base, bld->base.zero, bld->dst);
+               inv_dst = lp_build_comp(&bld->base, inv_dst);
+               bld->saturate = lp_build_min(&bld->base, src_alpha, inv_dst);
+            } else {
+               if (!bld->inv_dst) {
+                  bld->inv_dst = lp_build_comp(&bld->base, bld->dst);
+               }
+               bld->saturate = lp_build_min(&bld->base, src_alpha, bld->inv_dst);
+            }
+         }
          return bld->saturate;
       }
    case PIPE_BLENDFACTOR_CONST_COLOR:
-   case PIPE_BLENDFACTOR_CONST_ALPHA:
       return bld->const_;
+   case PIPE_BLENDFACTOR_CONST_ALPHA:
+      return const_alpha;
    case PIPE_BLENDFACTOR_SRC1_COLOR:
+      return bld->src1;
    case PIPE_BLENDFACTOR_SRC1_ALPHA:
-      /* TODO */
-      assert(0);
-      return bld->base.zero;
+      return src1_alpha;
    case PIPE_BLENDFACTOR_INV_SRC_COLOR:
-   case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
-      if(!bld->inv_src)
+      if (!bld->inv_src)
          bld->inv_src = lp_build_comp(&bld->base, bld->src);
       return bld->inv_src;
+   case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
+      if (!bld->inv_src_alpha)
+         bld->inv_src_alpha = lp_build_comp(&bld->base, src_alpha);
+      return bld->inv_src_alpha;
    case PIPE_BLENDFACTOR_INV_DST_COLOR:
    case PIPE_BLENDFACTOR_INV_DST_ALPHA:
-      if(!bld->inv_dst)
+      if (!bld->inv_dst)
          bld->inv_dst = lp_build_comp(&bld->base, bld->dst);
       return bld->inv_dst;
    case PIPE_BLENDFACTOR_INV_CONST_COLOR:
-   case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
-      if(!bld->inv_const)
+      if (!bld->inv_const)
          bld->inv_const = lp_build_comp(&bld->base, bld->const_);
       return bld->inv_const;
+   case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
+      if (!bld->inv_const_alpha)
+         bld->inv_const_alpha = lp_build_comp(&bld->base, const_alpha);
+      return bld->inv_const_alpha;
    case PIPE_BLENDFACTOR_INV_SRC1_COLOR:
+      return lp_build_comp(&bld->base, bld->src1);
    case PIPE_BLENDFACTOR_INV_SRC1_ALPHA:
-      /* TODO */
-      assert(0);
-      return bld->base.zero;
+      return lp_build_comp(&bld->base, src1_alpha);
    default:
       assert(0);
       return bld->base.zero;
@@ -190,7 +229,8 @@ lp_build_blend_swizzle(struct lp_build_blend_aos_context *bld,
                        LLVMValueRef rgb, 
                        LLVMValueRef alpha, 
                        enum lp_build_blend_swizzle rgb_swizzle,
-                       unsigned alpha_swizzle)
+                       unsigned alpha_swizzle,
+                       unsigned num_channels)
 {
    LLVMValueRef swizzled_rgb;
 
@@ -199,7 +239,7 @@ lp_build_blend_swizzle(struct lp_build_blend_aos_context *bld,
       swizzled_rgb = rgb;
       break;
    case LP_BUILD_BLEND_SWIZZLE_AAAA:
-      swizzled_rgb = lp_build_swizzle_scalar_aos(&bld->base, rgb, alpha_swizzle);
+      swizzled_rgb = lp_build_swizzle_scalar_aos(&bld->base, rgb, alpha_swizzle, num_channels);
       break;
    default:
       assert(0);
@@ -208,13 +248,13 @@ lp_build_blend_swizzle(struct lp_build_blend_aos_context *bld,
 
    if (rgb != alpha) {
       swizzled_rgb = lp_build_select_aos(&bld->base, 1 << alpha_swizzle,
-                                         alpha, swizzled_rgb);
+                                         alpha, swizzled_rgb,
+                                         num_channels);
    }
 
    return swizzled_rgb;
 }
 
-
 /**
  * @sa http://www.opengl.org/sdk/docs/man/xhtml/glBlendFuncSeparate.xml
  */
@@ -222,17 +262,23 @@ static LLVMValueRef
 lp_build_blend_factor(struct lp_build_blend_aos_context *bld,
                       unsigned rgb_factor,
                       unsigned alpha_factor,
-                      unsigned alpha_swizzle)
+                      unsigned alpha_swizzle,
+                      unsigned num_channels)
 {
    LLVMValueRef rgb_factor_, alpha_factor_;
    enum lp_build_blend_swizzle rgb_swizzle;
 
+   if (alpha_swizzle == PIPE_SWIZZLE_X && num_channels == 1) {
+      return lp_build_blend_factor_unswizzled(bld, alpha_factor, TRUE);
+   }
+
    rgb_factor_ = lp_build_blend_factor_unswizzled(bld, rgb_factor, FALSE);
 
-   if (alpha_swizzle != UTIL_FORMAT_SWIZZLE_NONE) {
+   if (alpha_swizzle != PIPE_SWIZZLE_NONE) {
       rgb_swizzle   = lp_build_blend_factor_swizzle(rgb_factor);
       alpha_factor_ = lp_build_blend_factor_unswizzled(bld, alpha_factor, TRUE);
-      return lp_build_blend_swizzle(bld, rgb_factor_, alpha_factor_, rgb_swizzle, alpha_swizzle);
+      return lp_build_blend_swizzle(bld, rgb_factor_, alpha_factor_, rgb_swizzle,
+                                    alpha_swizzle, num_channels);
    } else {
       return rgb_factor_;
    }
@@ -245,11 +291,15 @@ lp_build_blend_factor(struct lp_build_blend_aos_context *bld,
  * @param blend         the blend state of the shader variant
  * @param cbuf_format   format of the colour buffer
  * @param type          data type of the pixel vector
- * @param rt            rt number
+ * @param rt            render target index
  * @param src           blend src
+ * @param src_alpha     blend src alpha (if not included in src)
+ * @param src1          second blend src (for dual source blend)
+ * @param src1_alpha    second blend src alpha (if not included in src1)
  * @param dst           blend dst
  * @param mask          optional mask to apply to the blending result
  * @param const_        const blend color
+ * @param const_alpha   const blend color alpha (if not included in const_)
  * @param swizzle       swizzle values for RGBA
  *
  * @return the result of blending src and dst
@@ -257,47 +307,88 @@ lp_build_blend_factor(struct lp_build_blend_aos_context *bld,
 LLVMValueRef
 lp_build_blend_aos(struct gallivm_state *gallivm,
                    const struct pipe_blend_state *blend,
-                   const enum pipe_format *cbuf_format,
+                   enum pipe_format cbuf_format,
                    struct lp_type type,
                    unsigned rt,
                    LLVMValueRef src,
+                   LLVMValueRef src_alpha,
+                   LLVMValueRef src1,
+                   LLVMValueRef src1_alpha,
                    LLVMValueRef dst,
                    LLVMValueRef mask,
                    LLVMValueRef const_,
-                   const unsigned char swizzle[4])
+                   LLVMValueRef const_alpha,
+                   const unsigned char swizzle[4],
+                   int nr_channels)
 {
    const struct pipe_rt_blend_state * state = &blend->rt[rt];
+   const struct util_format_description * desc;
    struct lp_build_blend_aos_context bld;
    LLVMValueRef src_factor, dst_factor;
    LLVMValueRef result;
-   unsigned alpha_swizzle = swizzle[3];
-   boolean fullcolormask;
+   unsigned alpha_swizzle = PIPE_SWIZZLE_NONE;
+   unsigned i;
+
+   desc = util_format_description(cbuf_format);
 
    /* Setup build context */
    memset(&bld, 0, sizeof bld);
    lp_build_context_init(&bld.base, gallivm, type);
    bld.src = src;
+   bld.src1 = src1;
    bld.dst = dst;
    bld.const_ = const_;
+   bld.src_alpha = src_alpha;
+   bld.src1_alpha = src1_alpha;
+   bld.const_alpha = const_alpha;
+   bld.has_dst_alpha = FALSE;
+
+   /* Find the alpha channel if not provided separately */
+   if (!src_alpha) {
+      for (i = 0; i < 4; ++i) {
+         if (swizzle[i] == 3) {
+            alpha_swizzle = i;
+         }
+      }
+      /*
+       * Note that we may get src_alpha included from source (and 4 channels)
+       * even if the destination doesn't have an alpha channel (for rgbx
+       * formats). Generally this shouldn't make much of a difference (we're
+       * relying on blend factors being sanitized already if there's no
+       * dst alpha).
+       */
+      bld.has_dst_alpha = desc->swizzle[3] <= PIPE_SWIZZLE_W;
+   }
 
-   if (swizzle[3] > UTIL_FORMAT_SWIZZLE_W || swizzle[3] == swizzle[0])
-      alpha_swizzle = UTIL_FORMAT_SWIZZLE_NONE;
-
-   if (!state->blend_enable) {
+   if (blend->logicop_enable) {
+      if (!type.floating) {
+         result = lp_build_logicop(gallivm->builder, blend->logicop_func, src, dst);
+      }
+      else {
+         result = src;
+      }
+   } else if (!state->blend_enable) {
       result = src;
    } else {
-      boolean rgb_alpha_same = state->rgb_src_factor == state->rgb_dst_factor && state->alpha_src_factor == state->alpha_dst_factor;
-      assert(rgb_alpha_same || alpha_swizzle != UTIL_FORMAT_SWIZZLE_NONE);
+      boolean rgb_alpha_same = (state->rgb_src_factor == state->rgb_dst_factor &&
+                                state->alpha_src_factor == state->alpha_dst_factor) ||
+                               nr_channels == 1;
+      boolean alpha_only = nr_channels == 1 && alpha_swizzle == PIPE_SWIZZLE_X;
 
       src_factor = lp_build_blend_factor(&bld, state->rgb_src_factor,
-                                         state->alpha_src_factor, alpha_swizzle);
+                                         state->alpha_src_factor,
+                                         alpha_swizzle,
+                                         nr_channels);
+
       dst_factor = lp_build_blend_factor(&bld, state->rgb_dst_factor,
-                                         state->alpha_dst_factor, alpha_swizzle);
+                                         state->alpha_dst_factor,
+                                         alpha_swizzle,
+                                         nr_channels);
 
       result = lp_build_blend(&bld.base,
                               state->rgb_func,
-                              state->rgb_src_factor,
-                              state->rgb_dst_factor,
+                              alpha_only ? state->alpha_src_factor : state->rgb_src_factor,
+                              alpha_only ? state->alpha_dst_factor : state->rgb_dst_factor,
                               src,
                               dst,
                               src_factor,
@@ -305,7 +396,8 @@ lp_build_blend_aos(struct gallivm_state *gallivm,
                               rgb_alpha_same,
                               false);
 
-      if(state->rgb_func != state->alpha_func && alpha_swizzle != UTIL_FORMAT_SWIZZLE_NONE) {
+      if (state->rgb_func != state->alpha_func && nr_channels > 1 &&
+          alpha_swizzle != PIPE_SWIZZLE_NONE) {
          LLVMValueRef alpha;
 
          alpha = lp_build_blend(&bld.base,
@@ -323,22 +415,28 @@ lp_build_blend_aos(struct gallivm_state *gallivm,
                                          result,
                                          alpha,
                                          LP_BUILD_BLEND_SWIZZLE_RGBA,
-                                         alpha_swizzle);
+                                         alpha_swizzle,
+                                         nr_channels);
       }
    }
 
    /* Check if color mask is necessary */
-   fullcolormask = util_format_colormask_full(util_format_description(cbuf_format[rt]), state->colormask);
-
-   if (!fullcolormask) {
+   if (!util_format_colormask_full(desc, state->colormask)) {
       LLVMValueRef color_mask;
 
-      color_mask = lp_build_const_mask_aos_swizzled(gallivm, bld.base.type, state->colormask, swizzle);
+      color_mask = lp_build_const_mask_aos_swizzled(gallivm, bld.base.type,
+                                                    state->colormask, nr_channels, swizzle);
       lp_build_name(color_mask, "color_mask");
 
       /* Combine with input mask if necessary */
       if (mask) {
+         /* We can be blending floating values but masks are always integer... */
+         unsigned floating = bld.base.type.floating;
+         bld.base.type.floating = 0;
+
          mask = lp_build_and(&bld.base, color_mask, mask);
+
+         bld.base.type.floating = floating;
       } else {
          mask = color_mask;
       }