From 7d043162c5d9150947d9341cfa22192bd4c70fde Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Fonseca?= Date: Sat, 1 Aug 2009 17:59:19 +0100 Subject: [PATCH] llvmpipe: Blending. The code --- src/gallium/drivers/llvmpipe/Makefile | 1 + src/gallium/drivers/llvmpipe/SConscript | 14 +- src/gallium/drivers/llvmpipe/lp_bld.h | 12 + src/gallium/drivers/llvmpipe/lp_bld_blend.c | 319 ++++++++++ src/gallium/drivers/llvmpipe/lp_test_blend.c | 621 +++++++++++++++++++ 5 files changed, 965 insertions(+), 2 deletions(-) create mode 100644 src/gallium/drivers/llvmpipe/lp_bld_blend.c create mode 100644 src/gallium/drivers/llvmpipe/lp_test_blend.c diff --git a/src/gallium/drivers/llvmpipe/Makefile b/src/gallium/drivers/llvmpipe/Makefile index 8cbc54ccb72..1b6cd5ed85d 100644 --- a/src/gallium/drivers/llvmpipe/Makefile +++ b/src/gallium/drivers/llvmpipe/Makefile @@ -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 \ diff --git a/src/gallium/drivers/llvmpipe/SConscript b/src/gallium/drivers/llvmpipe/SConscript index e09608ed644..4a365e0cb3f 100644 --- a/src/gallium/drivers/llvmpipe/SConscript +++ b/src/gallium/drivers/llvmpipe/SConscript @@ -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') diff --git a/src/gallium/drivers/llvmpipe/lp_bld.h b/src/gallium/drivers/llvmpipe/lp_bld.h index e722e0b7a11..86571374b60 100644 --- a/src/gallium/drivers/llvmpipe/lp_bld.h +++ b/src/gallium/drivers/llvmpipe/lp_bld.h @@ -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 index 00000000000..d708047202f --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_bld_blend.c @@ -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 + */ + + +#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 index 00000000000..1a1313a8849 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_test_blend.c @@ -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 + * + * Blend computation code derived from code written by + * @author Brian Paul + */ + + +#include +#include + +#include +#include +#include +#include +#include +#include + +#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; +} -- 2.30.2