llvmpipe: Blending.
authorJosé Fonseca <jfonseca@vmware.com>
Sat, 1 Aug 2009 16:59:19 +0000 (17:59 +0100)
committerJosé Fonseca <jfonseca@vmware.com>
Sat, 29 Aug 2009 08:21:21 +0000 (09:21 +0100)
The code

src/gallium/drivers/llvmpipe/Makefile
src/gallium/drivers/llvmpipe/SConscript
src/gallium/drivers/llvmpipe/lp_bld.h
src/gallium/drivers/llvmpipe/lp_bld_blend.c [new file with mode: 0644]
src/gallium/drivers/llvmpipe/lp_test_blend.c [new file with mode: 0644]

index 8cbc54ccb72f092d2adbfe2fbd9c8e21efb12d4f..1b6cd5ed85d531a291ec78035c78f7c25bd3464e 100644 (file)
@@ -14,6 +14,7 @@ C_SOURCES = \
        lp_bld_store.c \
        lp_bld_loop.c \
        lp_bld_logicop.c \
+       lp_bld_blend.c \
        lp_clear.c \
        lp_flush.c \
        lp_query.c \
index e09608ed644e827e10c52c8747f2c5116c99e3d4..4a365e0cb3f52c34863cbe10065b8fe5bf3f7624 100644 (file)
@@ -2,7 +2,7 @@ Import('*')
 
 env = env.Clone()
 
-env.ParseConfig('llvm-config --cflags --libs jit interpreter nativecodegen')
+env.ParseConfig('llvm-config --cflags')
 
 llvmpipe = env.ConvenienceLibrary(
        target = 'llvmpipe',
@@ -17,6 +17,7 @@ llvmpipe = env.ConvenienceLibrary(
                'lp_bld_store.c',
                'lp_bld_loop.c',
                'lp_bld_logicop.c',
+               'lp_bld_blend.c',
                'lp_clear.c',
                'lp_context.c',
                'lp_draw_arrays.c',
@@ -45,12 +46,21 @@ llvmpipe = env.ConvenienceLibrary(
                'lp_tile_cache.c',
        ])
 
+
+env = env.Clone()
+
 env['LINK'] = env['CXX']
+env.ParseConfig('llvm-config --libs jit interpreter nativecodegen bitwriter')
+env.Prepend(LIBS = [llvmpipe] + auxiliaries)
 
 env.Program(
     target = 'lp_bld_test',
     source = ['lp_bld_test.c'],
-    LIBS = [llvmpipe] + auxiliaries + env['LIBS'],
+)
+
+env.Program(
+    target = 'lp_test_blend',
+    source = ['lp_test_blend.c'],
 )
 
 Export('llvmpipe')
index e722e0b7a111ea747dd68e1eb35d3d171ec20397..86571374b6077ae3746a99138ffe5ca17c843902 100644 (file)
@@ -45,6 +45,9 @@
 #include "pipe/p_format.h"
 
 
+struct pipe_blend_state;
+
+
 /**
  * Unpack a pixel into its RGBA components.
  *
@@ -128,4 +131,13 @@ lp_build_logicop(LLVMBuilderRef builder,
                  LLVMValueRef dst);
 
 
+LLVMValueRef
+lp_build_blend(LLVMBuilderRef builder,
+               const struct pipe_blend_state *blend,
+               LLVMValueRef src,
+               LLVMValueRef dst,
+               LLVMValueRef const_,
+               unsigned alpha_swizzle);
+
+
 #endif /* !LP_BLD_H */
diff --git a/src/gallium/drivers/llvmpipe/lp_bld_blend.c b/src/gallium/drivers/llvmpipe/lp_bld_blend.c
new file mode 100644 (file)
index 0000000..d708047
--- /dev/null
@@ -0,0 +1,319 @@
+/**************************************************************************
+ *
+ * Copyright 2009 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+/**
+ * @file
+ * Blend LLVM IR generation.
+ *
+ * This code is generic -- it should be able to cope both with floating point
+ * and integer inputs in AOS form.
+ *
+ * @author Jose Fonseca <jfonseca@vmware.com>
+ */
+
+
+#include "pipe/p_state.h"
+
+#include "lp_bld.h"
+#include "lp_bld_arit.h"
+
+
+/**
+ * We may the same values several times, so we keep them here to avoid
+ * recomputing them. Also reusing the values allows us to do simplifications
+ * that LLVM optimization passes wouldn't normally be able to do.
+ */
+struct lp_build_blend_values
+{
+   LLVMBuilderRef builder;
+   
+   LLVMValueRef undef;
+   LLVMValueRef zero;
+   LLVMValueRef one;
+
+   LLVMValueRef src;
+   LLVMValueRef dst;
+   LLVMValueRef const_;
+
+   LLVMValueRef inv_src;
+   LLVMValueRef inv_dst;
+   LLVMValueRef inv_const;
+   LLVMValueRef saturate;
+
+   LLVMValueRef rgb_src_factor;
+   LLVMValueRef alpha_src_factor;
+   LLVMValueRef rgb_dst_factor;
+   LLVMValueRef alpha_dst_factor;
+};
+
+
+static LLVMValueRef
+lp_build_blend_factor_unswizzled(struct lp_build_blend_values *values,
+                                 unsigned factor,
+                                 boolean alpha)
+{
+   switch (factor) {
+   case PIPE_BLENDFACTOR_ZERO:
+      return values->zero;
+   case PIPE_BLENDFACTOR_ONE:
+      return values->one;
+   case PIPE_BLENDFACTOR_SRC_COLOR:
+   case PIPE_BLENDFACTOR_SRC_ALPHA:
+      return values->src;
+   case PIPE_BLENDFACTOR_DST_COLOR:
+   case PIPE_BLENDFACTOR_DST_ALPHA:
+      return values->dst;
+   case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE:
+      if(alpha)
+         return values->one;
+      else {
+         if(!values->inv_dst)
+            values->inv_dst = lp_build_sub(values->builder, values->one, values->dst, values->zero);
+         if(!values->saturate)
+            values->saturate = lp_build_min_sat(values->builder, values->src, values->inv_dst, values->zero, values->one);
+         return values->saturate;
+      }
+   case PIPE_BLENDFACTOR_CONST_COLOR:
+   case PIPE_BLENDFACTOR_CONST_ALPHA:
+      return values->const_;
+   case PIPE_BLENDFACTOR_SRC1_COLOR:
+   case PIPE_BLENDFACTOR_SRC1_ALPHA:
+      /* TODO */
+      assert(0);
+      return values->zero;
+   case PIPE_BLENDFACTOR_INV_SRC_COLOR:
+   case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
+      if(!values->inv_src)
+         values->inv_src = lp_build_sub(values->builder, values->one, values->src, values->zero);
+      return values->inv_src;
+   case PIPE_BLENDFACTOR_INV_DST_COLOR:
+   case PIPE_BLENDFACTOR_INV_DST_ALPHA:
+      if(!values->inv_dst)
+         values->inv_dst = lp_build_sub(values->builder, values->one, values->dst, values->zero);
+      return values->inv_dst;
+   case PIPE_BLENDFACTOR_INV_CONST_COLOR:
+   case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
+      if(!values->inv_const)
+         values->inv_const = lp_build_sub(values->builder, values->one, values->const_, values->zero);
+      return values->inv_const;
+   case PIPE_BLENDFACTOR_INV_SRC1_COLOR:
+   case PIPE_BLENDFACTOR_INV_SRC1_ALPHA:
+      /* TODO */
+      assert(0);
+      return values->zero;
+   default:
+      assert(0);
+      return values->zero;
+   }
+}
+
+
+enum lp_build_blend_swizzle {
+   LP_BUILD_BLEND_SWIZZLE_RGBA = 0,
+   LP_BUILD_BLEND_SWIZZLE_AAAA = 1,
+};
+
+
+/**
+ * How should we shuffle the base factor.
+ */
+static enum lp_build_blend_swizzle
+lp_build_blend_factor_swizzle(unsigned factor)
+{
+   switch (factor) {
+   case PIPE_BLENDFACTOR_ONE:
+   case PIPE_BLENDFACTOR_ZERO:
+   case PIPE_BLENDFACTOR_SRC_COLOR:
+   case PIPE_BLENDFACTOR_DST_COLOR:
+   case PIPE_BLENDFACTOR_CONST_COLOR:
+   case PIPE_BLENDFACTOR_SRC1_COLOR:
+   case PIPE_BLENDFACTOR_INV_SRC_COLOR:
+   case PIPE_BLENDFACTOR_INV_DST_COLOR:
+   case PIPE_BLENDFACTOR_INV_CONST_COLOR:
+   case PIPE_BLENDFACTOR_INV_SRC1_COLOR:
+      return LP_BUILD_BLEND_SWIZZLE_RGBA;
+   case PIPE_BLENDFACTOR_SRC_ALPHA:
+   case PIPE_BLENDFACTOR_DST_ALPHA:
+   case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE:
+   case PIPE_BLENDFACTOR_SRC1_ALPHA:
+   case PIPE_BLENDFACTOR_CONST_ALPHA:
+   case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
+   case PIPE_BLENDFACTOR_INV_DST_ALPHA:
+   case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
+   case PIPE_BLENDFACTOR_INV_SRC1_ALPHA:
+      return LP_BUILD_BLEND_SWIZZLE_AAAA;
+   default:
+      assert(0);
+      return LP_BUILD_BLEND_SWIZZLE_RGBA;
+   }
+}
+
+
+static LLVMValueRef
+lp_build_blend_swizzle(struct lp_build_blend_values *values,
+                       LLVMValueRef rgb, 
+                       LLVMValueRef alpha, 
+                       enum lp_build_blend_swizzle rgb_swizzle,
+                       unsigned alpha_swizzle,
+                       unsigned n)
+{
+   LLVMValueRef swizzles[LP_MAX_VECTOR_SIZE];
+   unsigned i, j;
+
+   if(rgb == alpha) {
+      if(rgb_swizzle == LP_BUILD_BLEND_SWIZZLE_RGBA)
+         return rgb;
+
+      alpha = values->undef;
+   }
+
+   for(j = 0; j < n; j += 4) {
+      for(i = 0; i < 4; ++i) {
+         unsigned swizzle;
+
+         if(i == alpha_swizzle && alpha != values->undef) {
+            /* Take the alpha from the second shuffle argument */
+            swizzle = n + j + alpha_swizzle;
+         }
+         else if (rgb_swizzle == LP_BUILD_BLEND_SWIZZLE_AAAA) {
+            /* Take the alpha from the first shuffle argument */
+            swizzle = j + alpha_swizzle;
+         }
+         else {
+            swizzle = j + i;
+         }
+
+         swizzles[j + i] = LLVMConstInt(LLVMInt32Type(), swizzle, 0);
+      }
+   }
+
+   return LLVMBuildShuffleVector(values->builder, rgb, alpha, LLVMConstVector(swizzles, n), "");
+}
+
+
+static LLVMValueRef
+lp_build_blend_factor(struct lp_build_blend_values *values,
+                      LLVMValueRef factor1,
+                      unsigned rgb_factor,
+                      unsigned alpha_factor,
+                      unsigned alpha_swizzle,
+                      unsigned n)
+{
+   LLVMValueRef rgb_factor_;
+   LLVMValueRef alpha_factor_;
+   LLVMValueRef factor2;
+   enum lp_build_blend_swizzle rgb_swizzle;
+
+   rgb_factor_   = lp_build_blend_factor_unswizzled(values, rgb_factor,   FALSE);
+   alpha_factor_ = lp_build_blend_factor_unswizzled(values, alpha_factor, TRUE);
+
+   rgb_swizzle = lp_build_blend_factor_swizzle(rgb_factor);
+
+   factor2 = lp_build_blend_swizzle(values, rgb_factor_, alpha_factor_, rgb_swizzle, alpha_swizzle, n);
+
+   return lp_build_mul(values->builder, factor1, factor2, values->zero, values->one);
+}
+
+
+static LLVMValueRef
+lp_build_blend_func(struct lp_build_blend_values *values,
+                    unsigned func,
+                    LLVMValueRef term1, 
+                    LLVMValueRef term2)
+{
+   switch (func) {
+   case PIPE_BLEND_ADD:
+      return lp_build_add_sat(values->builder, term1, term2, values->zero, values->one);
+      break;
+   case PIPE_BLEND_SUBTRACT:
+      return lp_build_sub_sat(values->builder, term1, term2, values->zero, values->one);
+   case PIPE_BLEND_REVERSE_SUBTRACT:
+      return lp_build_sub_sat(values->builder, term2, term1, values->zero, values->one);
+   case PIPE_BLEND_MIN:
+      return lp_build_min_sat(values->builder, term1, term2, values->zero, values->one);
+   case PIPE_BLEND_MAX:
+      return lp_build_max_sat(values->builder, term1, term2, values->zero, values->one);
+   default:
+      assert(0);
+      return values->zero;
+   }
+}
+
+
+LLVMValueRef
+lp_build_blend(LLVMBuilderRef builder,
+               const struct pipe_blend_state *blend,
+               LLVMValueRef src,
+               LLVMValueRef dst,
+               LLVMValueRef const_,
+               unsigned alpha_swizzle)
+{
+   struct lp_build_blend_values values;
+   LLVMValueRef src_term;
+   LLVMValueRef dst_term;
+   LLVMTypeRef type;
+   unsigned n;
+
+   type = LLVMTypeOf(src);
+   n = LLVMGetVectorSize(type);
+
+   /*
+    * Compute constants
+    */
+   memset(&values, 0, sizeof values);
+   values.builder = builder;
+   values.undef = LLVMGetUndef(type);
+   values.zero = LLVMConstNull(type);
+   values.one = lp_build_const_aos(type, 1.0, 1.0, 1.0, 1.0, NULL);
+
+   values.src = src;
+   values.dst = dst;
+   values.const_ = const_;
+
+   /* TODO: There are still a few optimization oportunities here. For certain
+    * combinations it is possible to reorder the operations and therefor saving
+    * some instructions. */
+
+   src_term = lp_build_blend_factor(&values, src, blend->rgb_src_factor, blend->alpha_src_factor, alpha_swizzle, n);
+   dst_term = lp_build_blend_factor(&values, dst, blend->rgb_dst_factor, blend->alpha_dst_factor, alpha_swizzle, n);
+
+   if(blend->rgb_func == blend->alpha_func) {
+      return lp_build_blend_func(&values, blend->rgb_func, src_term, dst_term);
+   }
+   else {
+      /* Seperate RGB / A functions */
+
+      LLVMValueRef rgb;
+      LLVMValueRef alpha;
+
+      rgb   = lp_build_blend_func(&values, blend->rgb_func,   src_term, dst_term);
+      alpha = lp_build_blend_func(&values, blend->alpha_func, src_term, dst_term);
+
+      return lp_build_blend_swizzle(&values, rgb, alpha, LP_BUILD_BLEND_SWIZZLE_RGBA, alpha_swizzle, n);
+   }
+}
diff --git a/src/gallium/drivers/llvmpipe/lp_test_blend.c b/src/gallium/drivers/llvmpipe/lp_test_blend.c
new file mode 100644 (file)
index 0000000..1a1313a
--- /dev/null
@@ -0,0 +1,621 @@
+/**************************************************************************
+ *
+ * Copyright 2009 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+/**
+ * @file
+ * Unit tests for blend LLVM IR generation
+ *
+ * @author Jose Fonseca <jfonseca@vmware.com>
+ *
+ * Blend computation code derived from code written by
+ * @author Brian Paul <brian@vmware.com>
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <llvm-c/Core.h>
+#include <llvm-c/Analysis.h>
+#include <llvm-c/ExecutionEngine.h>
+#include <llvm-c/Target.h>
+#include <llvm-c/BitWriter.h>
+#include <llvm-c/Transforms/Scalar.h>
+
+#include "pipe/p_state.h"
+#include "util/u_format.h"
+#include "util/u_math.h"
+
+#include "lp_bld.h"
+
+
+unsigned verbose = 0;
+
+
+typedef void (*blend_test_ptr_t)(const float *src, const float *dst, const float *const_, float *res);
+
+
+static LLVMValueRef
+add_blend_test(LLVMModuleRef module,
+               const struct pipe_blend_state *blend)
+{
+   LLVMTypeRef args[4];
+   LLVMValueRef func;
+   LLVMValueRef src_ptr;
+   LLVMValueRef dst_ptr;
+   LLVMValueRef const_ptr;
+   LLVMValueRef res_ptr;
+   LLVMBasicBlockRef block;
+   LLVMBuilderRef builder;
+   LLVMValueRef src;
+   LLVMValueRef dst;
+   LLVMValueRef const_;
+   LLVMValueRef res;
+
+   args[0] = LLVMPointerType(LLVMVectorType(LLVMFloatType(), 4), 0);
+   args[1] = LLVMPointerType(LLVMVectorType(LLVMFloatType(), 4), 0);
+   args[2] = LLVMPointerType(LLVMVectorType(LLVMFloatType(), 4), 0);
+   args[3] = LLVMPointerType(LLVMVectorType(LLVMFloatType(), 4), 0);
+   func = LLVMAddFunction(module, "test", LLVMFunctionType(LLVMVoidType(), args, 4, 0));
+   LLVMSetFunctionCallConv(func, LLVMCCallConv);
+   src_ptr = LLVMGetParam(func, 0);
+   dst_ptr = LLVMGetParam(func, 1);
+   const_ptr = LLVMGetParam(func, 2);
+   res_ptr = LLVMGetParam(func, 3);
+
+   block = LLVMAppendBasicBlock(func, "entry");
+   builder = LLVMCreateBuilder();
+   LLVMPositionBuilderAtEnd(builder, block);
+
+   src = LLVMBuildLoad(builder, src_ptr, "src");
+   dst = LLVMBuildLoad(builder, dst_ptr, "dst");
+   const_ = LLVMBuildLoad(builder, const_ptr, "const");
+
+   res = lp_build_blend(builder, blend, src, dst, const_, 3);
+
+   LLVMSetValueName(res, "res");
+
+   LLVMBuildStore(builder, res, res_ptr);
+
+   LLVMBuildRetVoid(builder);
+
+   LLVMDisposeBuilder(builder);
+   return func;
+}
+
+
+static void
+random_color(float *color)
+{
+    color[0] = (float)((double)random()/(double)RAND_MAX);
+    color[1] = (float)((double)random()/(double)RAND_MAX);
+    color[2] = (float)((double)random()/(double)RAND_MAX);
+    color[3] = (float)((double)random()/(double)RAND_MAX);
+}
+
+
+/** Add and limit result to ceiling of 1.0 */
+#define ADD_SAT(R, A, B) \
+do { \
+   R = (A) + (B);  if (R > 1.0f) R = 1.0f; \
+} while (0)
+
+/** Subtract and limit result to floor of 0.0 */
+#define SUB_SAT(R, A, B) \
+do { \
+   R = (A) - (B);  if (R < 0.0f) R = 0.0f; \
+} while (0)
+
+
+static void
+compute_blend_ref_term(unsigned rgb_factor,
+                       unsigned alpha_factor,
+                       const float *factor,
+                       const float *src, 
+                       const float *dst, 
+                       const float *const_, 
+                       float *term)
+{
+   float temp;
+
+   switch (rgb_factor) {
+   case PIPE_BLENDFACTOR_ONE:
+      term[0] = factor[0]; /* R */
+      term[1] = factor[1]; /* G */
+      term[2] = factor[2]; /* B */
+      break;
+   case PIPE_BLENDFACTOR_SRC_COLOR:
+      term[0] = factor[0] * src[0]; /* R */
+      term[1] = factor[1] * src[1]; /* G */
+      term[2] = factor[2] * src[2]; /* B */
+      break;
+   case PIPE_BLENDFACTOR_SRC_ALPHA:
+      term[0] = factor[0] * src[3]; /* R */
+      term[1] = factor[1] * src[3]; /* G */
+      term[2] = factor[2] * src[3]; /* B */
+      break;
+   case PIPE_BLENDFACTOR_DST_COLOR:
+      term[0] = factor[0] * dst[0]; /* R */
+      term[1] = factor[1] * dst[1]; /* G */
+      term[2] = factor[2] * dst[2]; /* B */
+      break;
+   case PIPE_BLENDFACTOR_DST_ALPHA:
+      term[0] = factor[0] * dst[3]; /* R */
+      term[1] = factor[1] * dst[3]; /* G */
+      term[2] = factor[2] * dst[3]; /* B */
+      break;
+   case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE:
+      temp = MIN2(src[3], 1.0f - dst[3]);
+      term[0] = factor[0] * temp; /* R */
+      term[1] = factor[1] * temp; /* G */
+      term[2] = factor[2] * temp; /* B */
+      break;
+   case PIPE_BLENDFACTOR_CONST_COLOR:
+      term[0] = factor[0] * const_[0]; /* R */
+      term[1] = factor[1] * const_[1]; /* G */
+      term[2] = factor[2] * const_[2]; /* B */
+      break;
+   case PIPE_BLENDFACTOR_CONST_ALPHA:
+      term[0] = factor[0] * const_[3]; /* R */
+      term[1] = factor[1] * const_[3]; /* G */
+      term[2] = factor[2] * const_[3]; /* B */
+      break;
+   case PIPE_BLENDFACTOR_SRC1_COLOR:
+      assert(0); /* to do */
+      break;
+   case PIPE_BLENDFACTOR_SRC1_ALPHA:
+      assert(0); /* to do */
+      break;
+   case PIPE_BLENDFACTOR_ZERO:
+      term[0] = 0.0f; /* R */
+      term[1] = 0.0f; /* G */
+      term[2] = 0.0f; /* B */
+      break;
+   case PIPE_BLENDFACTOR_INV_SRC_COLOR:
+      term[0] = factor[0] * (1.0f - src[0]); /* R */
+      term[1] = factor[1] * (1.0f - src[1]); /* G */
+      term[2] = factor[2] * (1.0f - src[2]); /* B */
+      break;
+   case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
+      term[0] = factor[0] * (1.0f - src[3]); /* R */
+      term[1] = factor[1] * (1.0f - src[3]); /* G */
+      term[2] = factor[2] * (1.0f - src[3]); /* B */
+      break;
+   case PIPE_BLENDFACTOR_INV_DST_ALPHA:
+      term[0] = factor[0] * (1.0f - dst[3]); /* R */
+      term[1] = factor[1] * (1.0f - dst[3]); /* G */
+      term[2] = factor[2] * (1.0f - dst[3]); /* B */
+      break;
+   case PIPE_BLENDFACTOR_INV_DST_COLOR:
+      term[0] = factor[0] * (1.0f - dst[0]); /* R */
+      term[1] = factor[1] * (1.0f - dst[1]); /* G */
+      term[2] = factor[2] * (1.0f - dst[2]); /* B */
+      break;
+   case PIPE_BLENDFACTOR_INV_CONST_COLOR:
+      term[0] = factor[0] * (1.0f - const_[0]); /* R */
+      term[1] = factor[1] * (1.0f - const_[1]); /* G */
+      term[2] = factor[2] * (1.0f - const_[2]); /* B */
+      break;
+   case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
+      term[0] = factor[0] * (1.0f - const_[3]); /* R */
+      term[1] = factor[1] * (1.0f - const_[3]); /* G */
+      term[2] = factor[2] * (1.0f - const_[3]); /* B */
+      break;
+   case PIPE_BLENDFACTOR_INV_SRC1_COLOR:
+      assert(0); /* to do */
+      break;
+   case PIPE_BLENDFACTOR_INV_SRC1_ALPHA:
+      assert(0); /* to do */
+      break;
+   default:
+      assert(0);
+   }
+
+   /*
+    * Compute src/first term A
+    */
+   switch (alpha_factor) {
+   case PIPE_BLENDFACTOR_ONE:
+      term[3] = factor[3]; /* A */
+      break;
+   case PIPE_BLENDFACTOR_SRC_COLOR:
+   case PIPE_BLENDFACTOR_SRC_ALPHA:
+      term[3] = factor[3] * src[3]; /* A */
+      break;
+   case PIPE_BLENDFACTOR_DST_COLOR:
+   case PIPE_BLENDFACTOR_DST_ALPHA:
+      term[3] = factor[3] * dst[3]; /* A */
+      break;
+   case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE:
+      term[3] = src[3]; /* A */
+      break;
+   case PIPE_BLENDFACTOR_CONST_COLOR:
+   case PIPE_BLENDFACTOR_CONST_ALPHA:
+      term[3] = factor[3] * const_[3]; /* A */
+      break;
+   case PIPE_BLENDFACTOR_ZERO:
+      term[3] = 0.0f; /* A */
+      break;
+   case PIPE_BLENDFACTOR_INV_SRC_COLOR:
+   case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
+      term[3] = factor[3] * (1.0f - src[3]); /* A */
+      break;
+   case PIPE_BLENDFACTOR_INV_DST_COLOR:
+   case PIPE_BLENDFACTOR_INV_DST_ALPHA:
+      term[3] = factor[3] * (1.0f - dst[3]); /* A */
+      break;
+   case PIPE_BLENDFACTOR_INV_CONST_COLOR:
+   case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
+      term[3] = factor[3] * (1.0f - const_[3]);
+      break;
+   default:
+      assert(0);
+   }
+}
+
+
+static void
+compute_blend_ref(const struct pipe_blend_state *blend,
+                  const float *src, 
+                  const float *dst, 
+                  const float *const_, 
+                  float *res)
+{
+   float src_term[4];
+   float dst_term[4];
+
+   compute_blend_ref_term(blend->rgb_src_factor, blend->alpha_src_factor, src, src, dst, const_, src_term);
+   compute_blend_ref_term(blend->rgb_dst_factor, blend->alpha_dst_factor, dst, src, dst, const_, dst_term);
+
+   /*
+    * Combine RGB terms
+    */
+   switch (blend->rgb_func) {
+   case PIPE_BLEND_ADD:
+      ADD_SAT(res[0], src_term[0], dst_term[0]); /* R */
+      ADD_SAT(res[1], src_term[1], dst_term[1]); /* G */
+      ADD_SAT(res[2], src_term[2], dst_term[2]); /* B */
+      break;
+   case PIPE_BLEND_SUBTRACT:
+      SUB_SAT(res[0], src_term[0], dst_term[0]); /* R */
+      SUB_SAT(res[1], src_term[1], dst_term[1]); /* G */
+      SUB_SAT(res[2], src_term[2], dst_term[2]); /* B */
+      break;
+   case PIPE_BLEND_REVERSE_SUBTRACT:
+      SUB_SAT(res[0], dst_term[0], src_term[0]); /* R */
+      SUB_SAT(res[1], dst_term[1], src_term[1]); /* G */
+      SUB_SAT(res[2], dst_term[2], src_term[2]); /* B */
+      break;
+   case PIPE_BLEND_MIN:
+      res[0] = MIN2(src_term[0], dst_term[0]); /* R */
+      res[1] = MIN2(src_term[1], dst_term[1]); /* G */
+      res[2] = MIN2(src_term[2], dst_term[2]); /* B */
+      break;
+   case PIPE_BLEND_MAX:
+      res[0] = MAX2(src_term[0], dst_term[0]); /* R */
+      res[1] = MAX2(src_term[1], dst_term[1]); /* G */
+      res[2] = MAX2(src_term[2], dst_term[2]); /* B */
+      break;
+   default:
+      assert(0);
+   }
+
+   /*
+    * Combine A terms
+    */
+   switch (blend->alpha_func) {
+   case PIPE_BLEND_ADD:
+      ADD_SAT(res[3], src_term[3], dst_term[3]); /* A */
+      break;
+   case PIPE_BLEND_SUBTRACT:
+      SUB_SAT(res[3], src_term[3], dst_term[3]); /* A */
+      break;
+   case PIPE_BLEND_REVERSE_SUBTRACT:
+      SUB_SAT(res[3], dst_term[3], src_term[3]); /* A */
+      break;
+   case PIPE_BLEND_MIN:
+      res[3] = MIN2(src_term[3], dst_term[3]); /* A */
+      break;
+   case PIPE_BLEND_MAX:
+      res[3] = MAX2(src_term[3], dst_term[3]); /* A */
+      break;
+   default:
+      assert(0);
+   }
+}
+
+
+static boolean
+test_one(const struct pipe_blend_state *blend)
+{
+   LLVMModuleRef module = NULL;
+   LLVMValueRef func = NULL;
+   LLVMExecutionEngineRef engine = NULL;
+   LLVMModuleProviderRef provider = NULL;
+   LLVMPassManagerRef pass = NULL;
+   char *error = NULL;
+   blend_test_ptr_t blend_test_ptr;
+   boolean success;
+   unsigned i, j;
+
+   module = LLVMModuleCreateWithName("test");
+
+   func = add_blend_test(module, blend);
+
+   if(LLVMVerifyModule(module, LLVMPrintMessageAction, &error)) {
+      LLVMDumpModule(module);
+      LLVMDisposeMessage(error);
+      abort();
+   }
+
+   provider = LLVMCreateModuleProviderForExistingModule(module);
+   if (LLVMCreateJITCompiler(&engine, provider, 1, &error)) {
+      fprintf(stderr, "%s\n", error);
+      LLVMDisposeMessage(error);
+      abort();
+   }
+
+#if 0
+   pass = LLVMCreatePassManager();
+   LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), pass);
+   /* These are the passes currently listed in llvm-c/Transforms/Scalar.h,
+    * but there are more on SVN. */
+   LLVMAddConstantPropagationPass(pass);
+   LLVMAddInstructionCombiningPass(pass);
+   LLVMAddPromoteMemoryToRegisterPass(pass);
+   LLVMAddGVNPass(pass);
+   LLVMAddCFGSimplificationPass(pass);
+   LLVMRunPassManager(pass, module);
+#else
+   (void)pass;
+#endif
+
+   blend_test_ptr = (blend_test_ptr_t)LLVMGetPointerToGlobal(engine, func);
+
+   if(verbose >= 2)
+      LLVMDumpModule(module);
+
+   success = TRUE;
+   for(i = 0; i < 10; ++i) { 
+      float src[4];
+      float dst[4];
+      float const_[4];
+      float ref[4];
+      float res[4];
+
+      random_color(src);
+      random_color(dst);
+      random_color(const_);
+
+      compute_blend_ref(blend, src, dst, const_, ref);
+
+      blend_test_ptr(src, dst, const_, res);
+
+      for(j = 0; j < 4; ++j)
+         if(res[j] != ref[j])
+            success = FALSE;
+
+      if (!success) {
+         fprintf(stderr, "FAILED\n");
+         fprintf(stderr, "  Result: %f %f %f %f\n", res[0], res[1], res[2], res[3]);
+         fprintf(stderr, "          %f %f %f %f\n", ref[0], ref[1], ref[2], ref[3]);
+         LLVMDumpModule(module);
+         LLVMWriteBitcodeToFile(module, "blend.bc");
+         fprintf(stderr, "blend.bc written\n");
+         abort();
+         break;
+      }
+   }
+
+   LLVMDisposeExecutionEngine(engine);
+   //LLVMDisposeModule(module);
+
+   return success;
+}
+
+
+struct value_name_pair
+{
+   unsigned value;
+   const char *name;
+};
+
+
+const struct value_name_pair
+blend_factors[] = {
+   {PIPE_BLENDFACTOR_ZERO                , "zero"},
+   {PIPE_BLENDFACTOR_ONE                 , "one"},
+   {PIPE_BLENDFACTOR_SRC_COLOR           , "src_color"},
+   {PIPE_BLENDFACTOR_SRC_ALPHA           , "src_alpha"},
+   {PIPE_BLENDFACTOR_DST_COLOR           , "dst_color"},
+   {PIPE_BLENDFACTOR_DST_ALPHA           , "dst_alpha"},
+   {PIPE_BLENDFACTOR_CONST_COLOR         , "const_color"},
+   {PIPE_BLENDFACTOR_CONST_ALPHA         , "const_alpha"},
+#if 0
+   {PIPE_BLENDFACTOR_SRC1_COLOR          , "src1_color"},
+   {PIPE_BLENDFACTOR_SRC1_ALPHA          , "src1_alpha"},
+#endif
+   {PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE  , "src_alpha_saturate"},
+   {PIPE_BLENDFACTOR_INV_SRC_COLOR       , "inv_src_color"},
+   {PIPE_BLENDFACTOR_INV_SRC_ALPHA       , "inv_src_alpha"},
+   {PIPE_BLENDFACTOR_INV_DST_COLOR       , "inv_dst_color"},
+   {PIPE_BLENDFACTOR_INV_DST_ALPHA       , "inv_dst_alpha"},
+   {PIPE_BLENDFACTOR_INV_CONST_COLOR     , "inv_const_color"},
+   {PIPE_BLENDFACTOR_INV_CONST_ALPHA     , "inv_const_alpha"},
+#if 0
+   {PIPE_BLENDFACTOR_INV_SRC1_COLOR      , "inv_src1_color"},
+   {PIPE_BLENDFACTOR_INV_SRC1_ALPHA      , "inv_src1_alpha"}
+#endif
+};
+
+
+const struct value_name_pair
+blend_funcs[] = {
+   {PIPE_BLEND_ADD               , "add"},
+   {PIPE_BLEND_SUBTRACT          , "sub"},
+   {PIPE_BLEND_REVERSE_SUBTRACT  , "rev_sub"},
+   {PIPE_BLEND_MIN               , "min"},
+   {PIPE_BLEND_MAX               , "max"}
+};
+
+
+const unsigned num_funcs = sizeof(blend_funcs)/sizeof(blend_funcs[0]);
+const unsigned num_factors = sizeof(blend_factors)/sizeof(blend_factors[0]);
+
+
+static boolean 
+test_all(void)
+{
+   const struct value_name_pair *rgb_func;
+   const struct value_name_pair *rgb_src_factor;
+   const struct value_name_pair *rgb_dst_factor;
+   const struct value_name_pair *alpha_func;
+   const struct value_name_pair *alpha_src_factor;
+   const struct value_name_pair *alpha_dst_factor;
+   struct pipe_blend_state blend;
+   bool success = TRUE;
+
+   for(rgb_func = blend_funcs; rgb_func < &blend_funcs[num_funcs]; ++rgb_func) {
+      for(alpha_func = blend_funcs; alpha_func < &blend_funcs[num_funcs]; ++alpha_func) {
+         for(rgb_src_factor = blend_factors; rgb_src_factor < &blend_factors[num_factors]; ++rgb_src_factor) {
+            for(rgb_dst_factor = blend_factors; rgb_dst_factor <= rgb_src_factor; ++rgb_dst_factor) {
+               for(alpha_src_factor = blend_factors; alpha_src_factor < &blend_factors[num_factors]; ++alpha_src_factor) {
+                  for(alpha_dst_factor = blend_factors; alpha_dst_factor <= alpha_src_factor; ++alpha_dst_factor) {
+
+                     if(rgb_dst_factor->value == PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE ||
+                        alpha_dst_factor->value == PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE)
+                        continue;
+
+                     if(verbose >= 1)
+                        fprintf(stderr, 
+                                "%s=%s %s=%s %s=%s %s=%s %s=%s %s=%s ...\n",
+                                "rgb_func",         rgb_func->name,
+                                "rgb_src_factor",   rgb_src_factor->name,
+                                "rgb_dst_factor",   rgb_dst_factor->name,
+                                "alpha_func",       alpha_func->name,
+                                "alpha_src_factor", alpha_src_factor->name,
+                                "alpha_dst_factor", alpha_dst_factor->name);
+
+                     memset(&blend, 0, sizeof blend);
+                     blend.blend_enable      = 1;
+                     blend.rgb_func          = rgb_func->value;
+                     blend.rgb_src_factor    = rgb_src_factor->value;
+                     blend.rgb_dst_factor    = rgb_dst_factor->value;
+                     blend.alpha_func        = alpha_func->value;
+                     blend.alpha_src_factor  = alpha_src_factor->value;
+                     blend.alpha_dst_factor  = alpha_dst_factor->value;
+
+                     if(!test_one(&blend))
+                       success = FALSE;
+
+                  }
+               }
+            }
+         }
+      }
+   }
+
+   return success;
+}
+
+
+static boolean 
+test_some(unsigned long n)
+{
+   const struct value_name_pair *rgb_func;
+   const struct value_name_pair *rgb_src_factor;
+   const struct value_name_pair *rgb_dst_factor;
+   const struct value_name_pair *alpha_func;
+   const struct value_name_pair *alpha_src_factor;
+   const struct value_name_pair *alpha_dst_factor;
+   struct pipe_blend_state blend;
+   unsigned long i;
+   bool success = TRUE;
+
+   for(i = 0; i < n; ++i) {
+      rgb_func = &blend_funcs[random() % num_funcs];
+      alpha_func = &blend_funcs[random() % num_funcs];
+      rgb_src_factor = &blend_factors[random() % num_factors];
+      alpha_src_factor = &blend_factors[random() % num_factors];
+      
+      do {
+         rgb_dst_factor = &blend_factors[random() % num_factors];
+      } while(rgb_dst_factor->value == PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE);
+
+      do {
+         alpha_dst_factor = &blend_factors[random() % num_factors];
+      } while(alpha_dst_factor->value == PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE);
+
+      if(verbose >= 1)
+         fprintf(stderr, 
+                 "%s=%s %s=%s %s=%s %s=%s %s=%s %s=%s ...\n",
+                 "rgb_func",         rgb_func->name,
+                 "rgb_src_factor",   rgb_src_factor->name,
+                 "rgb_dst_factor",   rgb_dst_factor->name,
+                 "alpha_func",       alpha_func->name,
+                 "alpha_src_factor", alpha_src_factor->name,
+                 "alpha_dst_factor", alpha_dst_factor->name);
+
+      memset(&blend, 0, sizeof blend);
+      blend.blend_enable      = 1;
+      blend.rgb_func          = rgb_func->value;
+      blend.rgb_src_factor    = rgb_src_factor->value;
+      blend.rgb_dst_factor    = rgb_dst_factor->value;
+      blend.alpha_func        = alpha_func->value;
+      blend.alpha_src_factor  = alpha_src_factor->value;
+      blend.alpha_dst_factor  = alpha_dst_factor->value;
+
+      if(!test_one(&blend))
+        success = FALSE;
+
+   }
+
+   return success;
+}
+
+
+int main(int argc, char **argv)
+{
+   unsigned long n = 1000;
+   unsigned i;
+   boolean success;
+
+   for(i = 1; i < argc; ++i) {
+      if(strcmp(argv[i], "-v") == 0)
+         ++verbose;
+      else
+         n = atoi(argv[i]);
+   }
+      
+   if(n)
+      success = test_some(n);
+   else
+      success = test_all();
+
+   return success ? 0 : 1;
+}