1/**************************************************************************
2 *
3 * Copyright 2012 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28#include "pipe/p_state.h"
29#include "util/u_debug.h"
30
31#include "gallivm/lp_bld_type.h"
32#include "gallivm/lp_bld_arit.h"
33
34#include "lp_bld_blend.h"
35
36/**
37 * Is (a OP b) == (b OP a)?
38 */
39boolean
40lp_build_blend_func_commutative(unsigned func)
41{
42   switch (func) {
43   case PIPE_BLEND_ADD:
44   case PIPE_BLEND_MIN:
45   case PIPE_BLEND_MAX:
46      return TRUE;
47   case PIPE_BLEND_SUBTRACT:
48   case PIPE_BLEND_REVERSE_SUBTRACT:
49      return FALSE;
50   default:
51      assert(0);
52      return TRUE;
53   }
54}
55
56
57/**
58 * Whether the blending functions are the reverse of each other.
59 */
60boolean
61lp_build_blend_func_reverse(unsigned rgb_func, unsigned alpha_func)
62{
63   if(rgb_func == alpha_func)
64      return FALSE;
65   if(rgb_func == PIPE_BLEND_SUBTRACT && alpha_func == PIPE_BLEND_REVERSE_SUBTRACT)
66      return TRUE;
67   if(rgb_func == PIPE_BLEND_REVERSE_SUBTRACT && alpha_func == PIPE_BLEND_SUBTRACT)
68      return TRUE;
69   return FALSE;
70}
71
72
73/**
74 * Whether the blending factors are complementary of each other.
75 */
76static INLINE boolean
77lp_build_blend_factor_complementary(unsigned src_factor, unsigned dst_factor)
78{
79   return dst_factor == (src_factor ^ 0x10);
80}
81
82
83/**
84 * @sa http://www.opengl.org/sdk/docs/man/xhtml/glBlendEquationSeparate.xml
85 */
86LLVMValueRef
87lp_build_blend_func(struct lp_build_context *bld,
88                    unsigned func,
89                    LLVMValueRef term1,
90                    LLVMValueRef term2)
91{
92   switch (func) {
93   case PIPE_BLEND_ADD:
94      return lp_build_add(bld, term1, term2);
95   case PIPE_BLEND_SUBTRACT:
96      return lp_build_sub(bld, term1, term2);
97   case PIPE_BLEND_REVERSE_SUBTRACT:
98      return lp_build_sub(bld, term2, term1);
99   case PIPE_BLEND_MIN:
100      return lp_build_min(bld, term1, term2);
101   case PIPE_BLEND_MAX:
102      return lp_build_max(bld, term1, term2);
103   default:
104      assert(0);
105      return bld->zero;
106   }
107}
108
109
110/**
111 * Performs optimisations and blending independent of SoA/AoS
112 *
113 * @param func                   the blend function
114 * @param factor_src             PIPE_BLENDFACTOR_xxx
115 * @param factor_dst             PIPE_BLENDFACTOR_xxx
116 * @param src                    source rgba
117 * @param dst                    dest rgba
118 * @param src_factor             src factor computed value
119 * @param dst_factor             dst factor computed value
120 * @param not_alpha_dependent    same factors accross all channels of src/dst
121 *
122 * not_alpha_dependent should be:
123 *  SoA: always true as it is only one channel at a time
124 *  AoS: rgb_src_factor == alpha_src_factor && rgb_dst_factor == alpha_dst_factor
125 *
126 * Note that pretty much every possible optimisation can only be done on non-unorm targets
127 * due to unorm values not going above 1.0 meaning factorisation can change results.
128 * e.g. (0.9 * 0.9) + (0.9 * 0.9) != 0.9 * (0.9 + 0.9) as result of + is always <= 1.
129 */
130LLVMValueRef
131lp_build_blend(struct lp_build_context *bld,
132               unsigned func,
133               unsigned factor_src,
134               unsigned factor_dst,
135               LLVMValueRef src,
136               LLVMValueRef dst,
137               LLVMValueRef src_factor,
138               LLVMValueRef dst_factor,
139               boolean not_alpha_dependent,
140               boolean optimise_only)
141{
142   LLVMValueRef result, src_term, dst_term;
143
144   /* If we are not alpha dependent we can mess with the src/dst factors */
145   if (not_alpha_dependent) {
146      if (lp_build_blend_factor_complementary(factor_src, factor_dst)) {
147         if (func == PIPE_BLEND_ADD) {
148            if (factor_src < factor_dst) {
149               return lp_build_lerp(bld, src_factor, dst, src);
150            } else {
151               return lp_build_lerp(bld, dst_factor, src, dst);
152            }
153         } else if(bld->type.floating && func == PIPE_BLEND_SUBTRACT) {
154            result = lp_build_add(bld, src, dst);
155
156            if (factor_src < factor_dst) {
157               result = lp_build_mul(bld, result, src_factor);
158               return lp_build_sub(bld, result, dst);
159            } else {
160               result = lp_build_mul(bld, result, dst_factor);
161               return lp_build_sub(bld, src, result);
162            }
163         } else if(bld->type.floating && func == PIPE_BLEND_REVERSE_SUBTRACT) {
164            result = lp_build_add(bld, src, dst);
165
166            if (factor_src < factor_dst) {
167               result = lp_build_mul(bld, result, src_factor);
168               return lp_build_sub(bld, dst, result);
169            } else {
170               result = lp_build_mul(bld, result, dst_factor);
171               return lp_build_sub(bld, result, src);
172            }
173         }
174      }
175
176      if (bld->type.floating && factor_src == factor_dst) {
177         if (func == PIPE_BLEND_ADD ||
178             func == PIPE_BLEND_SUBTRACT ||
179             func == PIPE_BLEND_REVERSE_SUBTRACT) {
180            LLVMValueRef result;
181            result = lp_build_blend_func(bld, func, src, dst);
182            return lp_build_mul(bld, result, src_factor);
183         }
184      }
185   }
186
187   if (optimise_only)
188      return NULL;
189
190   src_term = lp_build_mul(bld, src, src_factor);
191   dst_term = lp_build_mul(bld, dst, dst_factor);
192   return lp_build_blend_func(bld, func, src_term, dst_term);
193}
194