| /************************************************************************** |
| * |
| * 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 -- AoS layout. |
| * |
| * AoS blending is in general much slower than SoA, but there are some cases |
| * where it might be faster. In particular, if a pixel is rendered only once |
| * then the overhead of tiling and untiling will dominate over the speedup that |
| * SoA gives. So we might want to detect such cases and fallback to AoS in the |
| * future, but for now this function is here for historical/benchmarking |
| * purposes. |
| * |
| * Run lp_blend_test after any change to this file. |
| * |
| * @author Jose Fonseca <jfonseca@vmware.com> |
| */ |
| |
| |
| #include "pipe/p_state.h" |
| |
| #include "lp_bld_type.h" |
| #include "lp_bld_const.h" |
| #include "lp_bld_arit.h" |
| #include "lp_bld_logic.h" |
| #include "lp_bld_swizzle.h" |
| #include "lp_bld_blend.h" |
| #include "lp_bld_debug.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_aos_context |
| { |
| struct lp_build_context base; |
| |
| 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_aos_context *bld, |
| unsigned factor, |
| boolean alpha) |
| { |
| 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_DST_COLOR: |
| case PIPE_BLENDFACTOR_DST_ALPHA: |
| return bld->dst; |
| case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE: |
| 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); |
| return bld->saturate; |
| } |
| case PIPE_BLENDFACTOR_CONST_COLOR: |
| case PIPE_BLENDFACTOR_CONST_ALPHA: |
| return bld->const_; |
| case PIPE_BLENDFACTOR_SRC1_COLOR: |
| case PIPE_BLENDFACTOR_SRC1_ALPHA: |
| /* TODO */ |
| assert(0); |
| return bld->base.zero; |
| case PIPE_BLENDFACTOR_INV_SRC_COLOR: |
| case PIPE_BLENDFACTOR_INV_SRC_ALPHA: |
| if(!bld->inv_src) |
| bld->inv_src = lp_build_comp(&bld->base, bld->src); |
| return bld->inv_src; |
| case PIPE_BLENDFACTOR_INV_DST_COLOR: |
| case PIPE_BLENDFACTOR_INV_DST_ALPHA: |
| 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) |
| bld->inv_const = lp_build_comp(&bld->base, bld->const_); |
| return bld->inv_const; |
| case PIPE_BLENDFACTOR_INV_SRC1_COLOR: |
| case PIPE_BLENDFACTOR_INV_SRC1_ALPHA: |
| /* TODO */ |
| assert(0); |
| return bld->base.zero; |
| default: |
| assert(0); |
| return bld->base.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_aos_context *bld, |
| LLVMValueRef rgb, |
| LLVMValueRef alpha, |
| enum lp_build_blend_swizzle rgb_swizzle, |
| unsigned alpha_swizzle) |
| { |
| if(rgb == alpha) { |
| if(rgb_swizzle == LP_BUILD_BLEND_SWIZZLE_RGBA) |
| return rgb; |
| if(rgb_swizzle == LP_BUILD_BLEND_SWIZZLE_AAAA) |
| return lp_build_broadcast_aos(&bld->base, rgb, alpha_swizzle); |
| } |
| else { |
| if(rgb_swizzle == LP_BUILD_BLEND_SWIZZLE_RGBA) { |
| boolean cond[4] = {0, 0, 0, 0}; |
| cond[alpha_swizzle] = 1; |
| return lp_build_select_aos(&bld->base, alpha, rgb, cond); |
| } |
| if(rgb_swizzle == LP_BUILD_BLEND_SWIZZLE_AAAA) { |
| unsigned char swizzle[4]; |
| swizzle[0] = alpha_swizzle; |
| swizzle[1] = alpha_swizzle; |
| swizzle[2] = alpha_swizzle; |
| swizzle[3] = alpha_swizzle; |
| swizzle[alpha_swizzle] += 4; |
| return lp_build_swizzle2_aos(&bld->base, rgb, alpha, swizzle); |
| } |
| } |
| assert(0); |
| return bld->base.undef; |
| } |
| |
| |
| /** |
| * @sa http://www.opengl.org/sdk/docs/man/xhtml/glBlendFuncSeparate.xml |
| */ |
| static LLVMValueRef |
| lp_build_blend_factor(struct lp_build_blend_aos_context *bld, |
| LLVMValueRef factor1, |
| unsigned rgb_factor, |
| unsigned alpha_factor, |
| unsigned alpha_swizzle) |
| { |
| LLVMValueRef rgb_factor_; |
| LLVMValueRef alpha_factor_; |
| LLVMValueRef factor2; |
| enum lp_build_blend_swizzle rgb_swizzle; |
| |
| rgb_factor_ = lp_build_blend_factor_unswizzled(bld, rgb_factor, FALSE); |
| alpha_factor_ = lp_build_blend_factor_unswizzled(bld, alpha_factor, TRUE); |
| |
| rgb_swizzle = lp_build_blend_factor_swizzle(rgb_factor); |
| |
| factor2 = lp_build_blend_swizzle(bld, rgb_factor_, alpha_factor_, rgb_swizzle, alpha_swizzle); |
| |
| return lp_build_mul(&bld->base, factor1, factor2); |
| } |
| |
| |
| boolean |
| lp_build_blend_func_commutative(unsigned func) |
| { |
| switch (func) { |
| case PIPE_BLEND_ADD: |
| case PIPE_BLEND_MIN: |
| case PIPE_BLEND_MAX: |
| return TRUE; |
| case PIPE_BLEND_SUBTRACT: |
| case PIPE_BLEND_REVERSE_SUBTRACT: |
| return FALSE; |
| default: |
| assert(0); |
| return TRUE; |
| } |
| } |
| |
| |
| boolean |
| lp_build_blend_func_reverse(unsigned rgb_func, unsigned alpha_func) |
| { |
| if(rgb_func == alpha_func) |
| return FALSE; |
| if(rgb_func == PIPE_BLEND_SUBTRACT && alpha_func == PIPE_BLEND_REVERSE_SUBTRACT) |
| return TRUE; |
| if(rgb_func == PIPE_BLEND_REVERSE_SUBTRACT && alpha_func == PIPE_BLEND_SUBTRACT) |
| return TRUE; |
| return FALSE; |
| } |
| |
| |
| /** |
| * @sa http://www.opengl.org/sdk/docs/man/xhtml/glBlendEquationSeparate.xml |
| */ |
| LLVMValueRef |
| lp_build_blend_func(struct lp_build_context *bld, |
| unsigned func, |
| LLVMValueRef term1, |
| LLVMValueRef term2) |
| { |
| switch (func) { |
| case PIPE_BLEND_ADD: |
| return lp_build_add(bld, term1, term2); |
| break; |
| case PIPE_BLEND_SUBTRACT: |
| return lp_build_sub(bld, term1, term2); |
| case PIPE_BLEND_REVERSE_SUBTRACT: |
| return lp_build_sub(bld, term2, term1); |
| case PIPE_BLEND_MIN: |
| return lp_build_min(bld, term1, term2); |
| case PIPE_BLEND_MAX: |
| return lp_build_max(bld, term1, term2); |
| default: |
| assert(0); |
| return bld->zero; |
| } |
| } |
| |
| |
| LLVMValueRef |
| lp_build_blend_aos(LLVMBuilderRef builder, |
| const struct pipe_blend_state *blend, |
| struct lp_type type, |
| LLVMValueRef src, |
| LLVMValueRef dst, |
| LLVMValueRef const_, |
| unsigned alpha_swizzle) |
| { |
| struct lp_build_blend_aos_context bld; |
| LLVMValueRef src_term; |
| LLVMValueRef dst_term; |
| |
| /* FIXME */ |
| assert(blend->colormask == 0xf); |
| |
| if(!blend->blend_enable) |
| return src; |
| |
| /* It makes no sense to blend unless values are normalized */ |
| assert(type.norm); |
| |
| /* Setup build context */ |
| memset(&bld, 0, sizeof bld); |
| lp_build_context_init(&bld.base, builder, type); |
| bld.src = src; |
| bld.dst = dst; |
| bld.const_ = const_; |
| |
| /* TODO: There are still a few optimization opportunities here. For certain |
| * combinations it is possible to reorder the operations and therefore saving |
| * some instructions. */ |
| |
| src_term = lp_build_blend_factor(&bld, src, blend->rgb_src_factor, blend->alpha_src_factor, alpha_swizzle); |
| dst_term = lp_build_blend_factor(&bld, dst, blend->rgb_dst_factor, blend->alpha_dst_factor, alpha_swizzle); |
| |
| lp_build_name(src_term, "src_term"); |
| lp_build_name(dst_term, "dst_term"); |
| |
| if(blend->rgb_func == blend->alpha_func) { |
| return lp_build_blend_func(&bld.base, blend->rgb_func, src_term, dst_term); |
| } |
| else { |
| /* Seperate RGB / A functions */ |
| |
| LLVMValueRef rgb; |
| LLVMValueRef alpha; |
| |
| rgb = lp_build_blend_func(&bld.base, blend->rgb_func, src_term, dst_term); |
| alpha = lp_build_blend_func(&bld.base, blend->alpha_func, src_term, dst_term); |
| |
| return lp_build_blend_swizzle(&bld, rgb, alpha, LP_BUILD_BLEND_SWIZZLE_RGBA, alpha_swizzle); |
| } |
| } |