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