1 /*
2  * © Copyright 2018 Alyssa Rosenzweig
3  * Copyright (C) 2019-2020 Collabora, Ltd.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  */
25 
26 #include <stdio.h>
27 #include "pan_blending.h"
28 #include "pan_context.h"
29 #include "gallium/auxiliary/util/u_blend.h"
30 #include "util/format/u_format.h"
31 
32 /* Implements fixed-function blending on Midgard. */
33 
34 /* Not all formats can be blended by fixed-function hardware */
35 
36 bool
panfrost_can_fixed_blend(enum pipe_format format)37 panfrost_can_fixed_blend(enum pipe_format format)
38 {
39         return panfrost_blend_format(format).internal != 0;
40 }
41 
42 /* Helper to find the uncomplemented Gallium blend factor corresponding to a
43  * complemented Gallium blend factor */
44 
45 static int
complement_factor(int factor)46 complement_factor(int factor)
47 {
48         switch (factor) {
49         case PIPE_BLENDFACTOR_INV_SRC_COLOR:
50                 return PIPE_BLENDFACTOR_SRC_COLOR;
51 
52         case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
53                 return PIPE_BLENDFACTOR_SRC_ALPHA;
54 
55         case PIPE_BLENDFACTOR_INV_DST_ALPHA:
56                 return PIPE_BLENDFACTOR_DST_ALPHA;
57 
58         case PIPE_BLENDFACTOR_INV_DST_COLOR:
59                 return PIPE_BLENDFACTOR_DST_COLOR;
60 
61         case PIPE_BLENDFACTOR_INV_CONST_COLOR:
62                 return PIPE_BLENDFACTOR_CONST_COLOR;
63 
64         case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
65                 return PIPE_BLENDFACTOR_CONST_ALPHA;
66 
67         default:
68                 return -1;
69         }
70 }
71 
72 /* Helper to strip the complement from any Gallium blend factor */
73 
74 static int
uncomplement_factor(int factor)75 uncomplement_factor(int factor)
76 {
77         int complement = complement_factor(factor);
78         return (complement == -1) ? factor : complement;
79 }
80 
81 /* Check if this is a special edge case blend factor, which may require the use
82  * of clip modifiers */
83 
84 static bool
is_edge_blendfactor(unsigned factor)85 is_edge_blendfactor(unsigned factor)
86 {
87         return factor == PIPE_BLENDFACTOR_ONE || factor == PIPE_BLENDFACTOR_ZERO;
88 }
89 
90 static bool
factor_is_supported(unsigned factor)91 factor_is_supported(unsigned factor)
92 {
93         return factor != PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE &&
94                factor != PIPE_BLENDFACTOR_SRC1_COLOR &&
95                factor != PIPE_BLENDFACTOR_SRC1_ALPHA &&
96                factor != PIPE_BLENDFACTOR_INV_SRC1_COLOR &&
97                factor != PIPE_BLENDFACTOR_INV_SRC1_ALPHA;
98 }
99 
100 static bool
can_use_fixed_function_blend(unsigned blend_func,unsigned src_factor,unsigned dest_factor)101 can_use_fixed_function_blend(unsigned blend_func,
102                              unsigned src_factor,
103                              unsigned dest_factor)
104 {
105         if (blend_func != PIPE_BLEND_ADD &&
106             blend_func != PIPE_BLEND_SUBTRACT &&
107             blend_func != PIPE_BLEND_REVERSE_SUBTRACT)
108                 return false;
109 
110         if (!factor_is_supported(src_factor) ||
111             !factor_is_supported(dest_factor))
112                 return false;
113 
114         if (src_factor != dest_factor &&
115             src_factor != complement_factor(dest_factor) &&
116             complement_factor(src_factor) != dest_factor &&
117             !is_edge_blendfactor(src_factor) &&
118             !is_edge_blendfactor(dest_factor))
119                 return false;
120 
121         return true;
122 }
123 
to_c_factor(unsigned factor,struct MALI_BLEND_FUNCTION * function)124 static void to_c_factor(unsigned factor, struct MALI_BLEND_FUNCTION *function)
125 {
126         if (complement_factor(factor) >= 0)
127                 function->invert_c = true;
128 
129         switch (uncomplement_factor(factor)) {
130         case PIPE_BLENDFACTOR_ONE:
131         case PIPE_BLENDFACTOR_ZERO:
132                 function->invert_c = factor == PIPE_BLENDFACTOR_ONE;
133                 function->c = MALI_BLEND_OPERAND_C_ZERO;
134                 break;
135 
136         case PIPE_BLENDFACTOR_SRC_ALPHA:
137                 function->c = MALI_BLEND_OPERAND_C_SRC_ALPHA;
138                 break;
139 
140         case PIPE_BLENDFACTOR_DST_ALPHA:
141                 function->c = MALI_BLEND_OPERAND_C_DEST_ALPHA;
142                 break;
143 
144         case PIPE_BLENDFACTOR_SRC_COLOR:
145                 function->c = MALI_BLEND_OPERAND_C_SRC;
146                 break;
147 
148         case PIPE_BLENDFACTOR_DST_COLOR:
149                 function->c = MALI_BLEND_OPERAND_C_DEST;
150                 break;
151 
152         case PIPE_BLENDFACTOR_CONST_COLOR:
153         case PIPE_BLENDFACTOR_CONST_ALPHA:
154                 function->c = MALI_BLEND_OPERAND_C_CONSTANT;
155                 break;
156         default:
157                 unreachable("Invalid blend factor");
158         }
159 
160 }
161 
162 static bool
to_panfrost_function(unsigned blend_func,unsigned src_factor,unsigned dest_factor,struct MALI_BLEND_FUNCTION * function)163 to_panfrost_function(unsigned blend_func,
164                      unsigned src_factor,
165                      unsigned dest_factor,
166                      struct MALI_BLEND_FUNCTION *function)
167 {
168         if (!can_use_fixed_function_blend(blend_func, src_factor, dest_factor))
169                 return false;
170 
171         if (src_factor == PIPE_BLENDFACTOR_ZERO) {
172                 function->a = MALI_BLEND_OPERAND_A_ZERO;
173                 function->b = MALI_BLEND_OPERAND_B_DEST;
174                 if (blend_func == PIPE_BLEND_SUBTRACT)
175                         function->negate_b = true;
176                 to_c_factor(dest_factor, function);
177         } else if (src_factor == PIPE_BLENDFACTOR_ONE) {
178                 function->a = MALI_BLEND_OPERAND_A_SRC;
179                 function->b = MALI_BLEND_OPERAND_B_DEST;
180                 if (blend_func == PIPE_BLEND_SUBTRACT)
181                         function->negate_b = true;
182                 else if (blend_func == PIPE_BLEND_REVERSE_SUBTRACT)
183                         function->negate_a = true;
184                 to_c_factor(dest_factor, function);
185         } else if (dest_factor == PIPE_BLENDFACTOR_ZERO) {
186                 function->a = MALI_BLEND_OPERAND_A_ZERO;
187                 function->b = MALI_BLEND_OPERAND_B_SRC;
188                 if (blend_func == PIPE_BLEND_REVERSE_SUBTRACT)
189                         function->negate_b = true;
190                 to_c_factor(src_factor, function);
191         } else if (dest_factor == PIPE_BLENDFACTOR_ONE) {
192                 function->a = MALI_BLEND_OPERAND_A_DEST;
193                 function->b = MALI_BLEND_OPERAND_B_SRC;
194                 if (blend_func == PIPE_BLEND_SUBTRACT)
195                         function->negate_a = true;
196                 else if (blend_func == PIPE_BLEND_REVERSE_SUBTRACT)
197                         function->negate_b = true;
198                 to_c_factor(src_factor, function);
199         } else if (src_factor == dest_factor) {
200                 function->a = MALI_BLEND_OPERAND_A_ZERO;
201                 to_c_factor(src_factor, function);
202 
203                 switch (blend_func) {
204                 case PIPE_BLEND_ADD:
205                         function->b = MALI_BLEND_OPERAND_B_SRC_PLUS_DEST;
206                         break;
207                 case PIPE_BLEND_REVERSE_SUBTRACT:
208                         function->negate_b = true;
209                         /* fall-through */
210                 case PIPE_BLEND_SUBTRACT:
211                         function->b = MALI_BLEND_OPERAND_B_SRC_MINUS_DEST;
212                         break;
213                 default:
214                         unreachable("Invalid blend function");
215                 }
216         } else {
217                 assert(src_factor == complement_factor(dest_factor) ||
218                        complement_factor(src_factor) == dest_factor);
219 
220                 function->a = MALI_BLEND_OPERAND_A_DEST;
221                 to_c_factor(src_factor, function);
222 
223                 switch (blend_func) {
224                 case PIPE_BLEND_ADD:
225                         function->b = MALI_BLEND_OPERAND_B_SRC_MINUS_DEST;
226                         break;
227                 case PIPE_BLEND_REVERSE_SUBTRACT:
228                         function->b = MALI_BLEND_OPERAND_B_SRC_PLUS_DEST;
229                         function->negate_b = true;
230                         break;
231                 case PIPE_BLEND_SUBTRACT:
232                         function->b = MALI_BLEND_OPERAND_B_SRC_PLUS_DEST;
233                         function->negate_a = true;
234                         break;
235                 }
236         }
237 
238         return true;
239 }
240 
241 /* We can upload a single constant for all of the factors. So, scan
242  * the factors for constants used to create a mask to check later. */
243 
244 static unsigned
panfrost_blend_factor_constant_mask(enum pipe_blendfactor factor)245 panfrost_blend_factor_constant_mask(enum pipe_blendfactor factor)
246 {
247         unsigned mask = 0;
248 
249         factor = uncomplement_factor(factor);
250         if (factor == PIPE_BLENDFACTOR_CONST_COLOR)
251                 mask |= 0b0111; /* RGB */
252         else if (factor == PIPE_BLENDFACTOR_CONST_ALPHA)
253                 mask |= 0b1000; /* A */
254 
255         return mask;
256 }
257 
258 unsigned
panfrost_blend_constant_mask(const struct pipe_rt_blend_state * blend)259 panfrost_blend_constant_mask(const struct pipe_rt_blend_state *blend)
260 {
261         return panfrost_blend_factor_constant_mask(blend->rgb_src_factor) |
262                panfrost_blend_factor_constant_mask(blend->rgb_dst_factor) |
263                panfrost_blend_factor_constant_mask(blend->alpha_src_factor) |
264                panfrost_blend_factor_constant_mask(blend->alpha_dst_factor);
265 }
266 
267 /* Create the descriptor for a fixed blend mode given the corresponding Gallium
268  * state, if possible. Return true and write out the blend descriptor into
269  * blend_equation. If it is not possible with the fixed function
270  * representating, return false to handle degenerate cases with a blend shader
271  */
272 
273 bool
panfrost_make_fixed_blend_mode(const struct pipe_rt_blend_state blend,struct MALI_BLEND_EQUATION * equation)274 panfrost_make_fixed_blend_mode(const struct pipe_rt_blend_state blend,
275                                struct MALI_BLEND_EQUATION *equation)
276 {
277         /* If no blending is enabled, default back on `replace` mode */
278 
279         if (!blend.blend_enable) {
280                 equation->color_mask = blend.colormask;
281                 equation->rgb.a = MALI_BLEND_OPERAND_A_SRC;
282                 equation->rgb.b = MALI_BLEND_OPERAND_B_SRC;
283                 equation->rgb.c = MALI_BLEND_OPERAND_C_ZERO;
284                 equation->alpha.a = MALI_BLEND_OPERAND_A_SRC;
285                 equation->alpha.b = MALI_BLEND_OPERAND_B_SRC;
286                 equation->alpha.c = MALI_BLEND_OPERAND_C_ZERO;
287                 return true;
288         }
289 
290         /* Try to compile the actual fixed-function blend */
291         if (!to_panfrost_function(blend.rgb_func, blend.rgb_src_factor,
292                                   blend.rgb_dst_factor,
293                                   &equation->rgb))
294                 return false;
295 
296         if (!to_panfrost_function(blend.alpha_func, blend.alpha_src_factor,
297                                   blend.alpha_dst_factor,
298                                   &equation->alpha))
299                 return false;
300 
301         equation->color_mask = blend.colormask;
302         return true;
303 }
304