1 /*
2  * © Copyright 2018 Alyssa Rosenzweig
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  *
23  */
24 
25 #include <stdio.h>
26 #include "pan_blending.h"
27 #include "pan_context.h"
28 #include "gallium/auxiliary/util/u_blend.h"
29 #include "util/format/u_format.h"
30 
31 /*
32  * Implements fixed-function blending on Midgard.
33  *
34  * Midgard splits blending into a fixed-function fast path and a programmable
35  * slow path. The fixed function blending architecture is based on "dominant"
36  * blend factors. Blending is encoded separately (but identically) between RGB
37  * and alpha functions.
38  *
39  * Essentially, for a given blending operation, there is a single dominant
40  * factor. The following dominant factors are possible:
41  *
42  * 	- zero
43  * 	- source color
44  * 	- destination color
45  * 	- source alpha
46  * 	- destination alpha
47  * 	- constant float
48  *
49  * Further, a dominant factor's arithmetic compliment could be used. For
50  * instance, to encode GL_ONE_MINUS_SOURCE_ALPHA, the dominant factor would be
51  * MALI_DOMINANT_SRC_ALPHA with the complement_dominant bit set.
52  *
53  * A single constant float can be passed to the fixed-function hardware,
54  * allowing CONSTANT_ALPHA support. Further, if all components of the constant
55  * glBlendColor are identical, CONSTANT_COLOR can be implemented with the
56  * constant float mode. If the components differ, programmable blending is
57  * required.
58  *
59  * The nondominant factor can be either:
60  *
61  * 	- the same as the dominant factor (MALI_BLEND_NON_MIRROR)
62  * 	- zero (MALI_BLEND_NON_ZERO)
63  *
64  * Exactly one of the blend operation's source or destination can be used as
65  * the dominant factor; this is selected by the
66  * MALI_BLEND_DOM_SOURCE/DESTINATION flag.
67  *
68  * By default, all blending follows the standard OpenGL addition equation:
69  *
70  * 	out = source_value * source_factor + destination_value * destination_factor
71  *
72  * By setting the negate_source or negate_dest bits, other blend functions can
73  * be created. For instance, for SUBTRACT mode, set the "negate destination"
74  * flag, and similarly for REVERSE_SUBTRACT with "negate source".
75  *
76  * Finally, there is a "clip modifier" controlling the final blending
77  * behaviour, allowing for the following modes:
78  *
79  * 	- normal
80  * 	- force source factor to one (MALI_BLEND_MODE_SOURCE_ONE)
81  * 	- force destination factor to one (MALI_BLEND_MODE_DEST_ONE)
82  *
83  * The clipping flags can be used to encode blend modes where the nondominant
84  * factor is ONE.
85  *
86  * As an example putting it all together, to encode the following blend state:
87  *
88  * 	glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
89  * 	glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ONE);
90  *
91  * We need the following configuration:
92  *
93  * 	- negate source (for REVERSE_SUBTRACT)
94  * 	- dominant factor "source alpha"
95  * 		- complement dominant
96  * 		- source dominant
97  * 	- force destination to ONE
98  *
99  * The following routines implement this fixed function blending encoding
100  */
101 
102 /* Not all formats can be blended by fixed-function hardware */
103 
104 bool
panfrost_can_fixed_blend(enum pipe_format format)105 panfrost_can_fixed_blend(enum pipe_format format)
106 {
107         /* Fixed-function can handle sRGB */
108         format = util_format_linear(format);
109 
110         switch (panfrost_pipe_format_table[format].hw) {
111         case MALI_RGB565:
112         case MALI_RGB5_A1_UNORM:
113         case MALI_RGB10_A2_UNORM:
114         case MALI_RGBA4_UNORM:
115         case MALI_R8_UNORM:
116         case MALI_RG8_UNORM:
117         case MALI_RGB8_UNORM:
118         case MALI_RGBA8_UNORM:
119                 return true;
120         default:
121                 return false;
122         }
123 }
124 
125 /* Helper to find the uncomplemented Gallium blend factor corresponding to a
126  * complemented Gallium blend factor */
127 
128 static int
complement_factor(int factor)129 complement_factor(int factor)
130 {
131         switch (factor) {
132         case PIPE_BLENDFACTOR_INV_SRC_COLOR:
133                 return PIPE_BLENDFACTOR_SRC_COLOR;
134 
135         case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
136                 return PIPE_BLENDFACTOR_SRC_ALPHA;
137 
138         case PIPE_BLENDFACTOR_INV_DST_ALPHA:
139                 return PIPE_BLENDFACTOR_DST_ALPHA;
140 
141         case PIPE_BLENDFACTOR_INV_DST_COLOR:
142                 return PIPE_BLENDFACTOR_DST_COLOR;
143 
144         case PIPE_BLENDFACTOR_INV_CONST_COLOR:
145                 return PIPE_BLENDFACTOR_CONST_COLOR;
146 
147         case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
148                 return PIPE_BLENDFACTOR_CONST_ALPHA;
149 
150         default:
151                 return -1;
152         }
153 }
154 
155 /* Helper to strip the complement from any Gallium blend factor */
156 
157 static int
uncomplement_factor(int factor)158 uncomplement_factor(int factor)
159 {
160         int complement = complement_factor(factor);
161         return (complement == -1) ? factor : complement;
162 }
163 
164 
165 /* Attempt to find the dominant factor given a particular factor, complementing
166  * as necessary */
167 
168 static bool
panfrost_make_dominant_factor(unsigned src_factor,enum mali_dominant_factor * factor)169 panfrost_make_dominant_factor(unsigned src_factor, enum mali_dominant_factor *factor)
170 {
171         switch (src_factor) {
172         case PIPE_BLENDFACTOR_SRC_COLOR:
173         case PIPE_BLENDFACTOR_INV_SRC_COLOR:
174                 *factor = MALI_DOMINANT_SRC_COLOR;
175                 break;
176 
177         case PIPE_BLENDFACTOR_SRC_ALPHA:
178         case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
179                 *factor = MALI_DOMINANT_SRC_ALPHA;
180                 break;
181 
182         case PIPE_BLENDFACTOR_DST_COLOR:
183         case PIPE_BLENDFACTOR_INV_DST_COLOR:
184                 *factor = MALI_DOMINANT_DST_COLOR;
185                 break;
186 
187         case PIPE_BLENDFACTOR_DST_ALPHA:
188         case PIPE_BLENDFACTOR_INV_DST_ALPHA:
189                 *factor = MALI_DOMINANT_DST_ALPHA;
190                 break;
191 
192         case PIPE_BLENDFACTOR_ONE:
193         case PIPE_BLENDFACTOR_ZERO:
194                 *factor = MALI_DOMINANT_ZERO;
195                 break;
196 
197         case PIPE_BLENDFACTOR_CONST_ALPHA:
198         case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
199         case PIPE_BLENDFACTOR_CONST_COLOR:
200         case PIPE_BLENDFACTOR_INV_CONST_COLOR:
201                 *factor = MALI_DOMINANT_CONSTANT;
202                 break;
203 
204         default:
205                 /* Fancy blend modes not supported */
206                 return false;
207         }
208 
209         return true;
210 }
211 
212 /* Check if this is a special edge case blend factor, which may require the use
213  * of clip modifiers */
214 
215 static bool
is_edge_blendfactor(unsigned factor)216 is_edge_blendfactor(unsigned factor)
217 {
218         return factor == PIPE_BLENDFACTOR_ONE || factor == PIPE_BLENDFACTOR_ZERO;
219 }
220 
221 /* Perform the actual fixed function encoding. Encode the function with negate
222  * bits. Check for various cases to work out the dominant/nondominant split and
223  * accompanying flags. */
224 
225 static bool
panfrost_make_fixed_blend_part(unsigned func,unsigned src_factor,unsigned dst_factor,unsigned * out)226 panfrost_make_fixed_blend_part(unsigned func, unsigned src_factor, unsigned dst_factor, unsigned *out)
227 {
228         struct mali_blend_mode part = { 0 };
229 
230         /* Make sure that the blend function is representible */
231 
232         switch (func) {
233         case PIPE_BLEND_ADD:
234                 break;
235 
236         /* TODO: Reenable subtraction modes when those fixed */
237         case PIPE_BLEND_SUBTRACT:
238         case PIPE_BLEND_REVERSE_SUBTRACT:
239         default:
240                 return false;
241         }
242 
243         part.clip_modifier = MALI_BLEND_MOD_NORMAL;
244 
245         /* Decide which is dominant, source or destination. If one is an edge
246          * case, use the other as a factor. If they're the same, it doesn't
247          * matter; we just mirror. If they're different non-edge-cases, you
248          * need a blend shader (don't do that). */
249 
250         if (is_edge_blendfactor(dst_factor)) {
251                 part.dominant = MALI_BLEND_DOM_SOURCE;
252                 part.nondominant_mode = MALI_BLEND_NON_ZERO;
253 
254                 if (dst_factor == PIPE_BLENDFACTOR_ONE)
255                         part.clip_modifier = MALI_BLEND_MOD_DEST_ONE;
256         } else if (is_edge_blendfactor(src_factor)) {
257                 part.dominant = MALI_BLEND_DOM_DESTINATION;
258                 part.nondominant_mode = MALI_BLEND_NON_ZERO;
259 
260                 if (src_factor == PIPE_BLENDFACTOR_ONE)
261                         part.clip_modifier = MALI_BLEND_MOD_SOURCE_ONE;
262         } else if (src_factor == dst_factor) {
263                 /* XXX: Why? */
264                 part.dominant = func == PIPE_BLEND_ADD ?
265                                 MALI_BLEND_DOM_DESTINATION : MALI_BLEND_DOM_SOURCE;
266 
267                 part.nondominant_mode = MALI_BLEND_NON_MIRROR;
268         } else if (src_factor == complement_factor(dst_factor)) {
269                 /* TODO: How does this work exactly? */
270                 part.dominant = MALI_BLEND_DOM_SOURCE;
271                 part.nondominant_mode = MALI_BLEND_NON_MIRROR;
272                 part.clip_modifier = MALI_BLEND_MOD_DEST_ONE;
273 
274                 /* The complement is handled by the clip modifier, don't set a
275                  * complement flag */
276 
277                 dst_factor = src_factor;
278         } else if (dst_factor == complement_factor(src_factor)) {
279                 part.dominant = MALI_BLEND_DOM_SOURCE;
280                 part.nondominant_mode = MALI_BLEND_NON_MIRROR;
281                 part.clip_modifier = MALI_BLEND_MOD_SOURCE_ONE;
282 
283                 src_factor = dst_factor;
284         } else {
285                 return false;
286         }
287 
288         unsigned in_dominant_factor =
289                 part.dominant == MALI_BLEND_DOM_SOURCE ? src_factor : dst_factor;
290 
291         if (part.clip_modifier == MALI_BLEND_MOD_NORMAL && in_dominant_factor == PIPE_BLENDFACTOR_ONE) {
292                 part.clip_modifier = part.dominant == MALI_BLEND_DOM_SOURCE ? MALI_BLEND_MOD_SOURCE_ONE : MALI_BLEND_MOD_DEST_ONE;
293                 in_dominant_factor = PIPE_BLENDFACTOR_ZERO;
294         }
295 
296         enum mali_dominant_factor dominant_factor;
297 
298         if (!panfrost_make_dominant_factor(in_dominant_factor, &dominant_factor))
299                 return false;
300 
301         part.dominant_factor = dominant_factor;
302         part.complement_dominant = util_blend_factor_is_inverted(in_dominant_factor);
303 
304         /* It's not clear what this does, but fixes some ADD blending tests.
305          * More research is needed XXX */
306 
307         if ((part.clip_modifier == MALI_BLEND_MOD_SOURCE_ONE) && (part.dominant == MALI_BLEND_DOM_SOURCE))
308                 part.negate_dest = true;
309 
310         /* Write out mode */
311         memcpy(out, &part, sizeof(part));
312 
313         return true;
314 }
315 
316 /* We can upload a single constant for all of the factors. So, scan
317  * the factors for constants used to create a mask to check later. */
318 
319 static unsigned
panfrost_constant_mask(unsigned * factors,unsigned num_factors)320 panfrost_constant_mask(unsigned *factors, unsigned num_factors)
321 {
322         unsigned mask = 0;
323 
324         for (unsigned i = 0; i < num_factors; ++i) {
325                 unsigned factor = uncomplement_factor(factors[i]);
326 
327                 if (factor == PIPE_BLENDFACTOR_CONST_COLOR)
328                         mask |= 0b0111; /* RGB */
329                 else if (factor == PIPE_BLENDFACTOR_CONST_ALPHA)
330                         mask |= 0b1000; /* A */
331         }
332 
333         return mask;
334 }
335 
336 /* Create the descriptor for a fixed blend mode given the corresponding Gallium
337  * state, if possible. Return true and write out the blend descriptor into
338  * blend_equation. If it is not possible with the fixed function
339  * representating, return false to handle degenerate cases with a blend shader
340  */
341 
342 bool
panfrost_make_fixed_blend_mode(const struct pipe_rt_blend_state * blend,struct mali_blend_equation * out,unsigned * constant_mask,unsigned colormask)343 panfrost_make_fixed_blend_mode(
344         const struct pipe_rt_blend_state *blend,
345         struct mali_blend_equation *out,
346         unsigned *constant_mask,
347         unsigned colormask)
348 {
349         /* Gallium and Mali represent colour masks identically. XXX: Static
350          * assert for future proof */
351 
352         out->color_mask = colormask;
353 
354         /* If no blending is enabled, default back on `replace` mode */
355 
356         if (!blend->blend_enable) {
357                 out->rgb_mode = 0x122;
358                 out->alpha_mode = 0x122;
359                 return true;
360         }
361 
362         /* At draw-time, we'll need to analyze the blend constant, so
363          * precompute a mask for it -- even if we don't end up able to use
364          * fixed-function blending */
365 
366         unsigned factors[] = {
367                 blend->rgb_src_factor, blend->rgb_dst_factor,
368                 blend->alpha_src_factor, blend->alpha_dst_factor,
369         };
370 
371         *constant_mask = panfrost_constant_mask(factors, ARRAY_SIZE(factors));
372 
373         /* Try to compile the actual fixed-function blend */
374 
375         unsigned rgb_mode = 0;
376         unsigned alpha_mode = 0;
377 
378         if (!panfrost_make_fixed_blend_part(
379                     blend->rgb_func, blend->rgb_src_factor, blend->rgb_dst_factor,
380                     &rgb_mode))
381                 return false;
382 
383         if (!panfrost_make_fixed_blend_part(
384                     blend->alpha_func, blend->alpha_src_factor, blend->alpha_dst_factor,
385                     &alpha_mode))
386                 return false;
387 
388         out->rgb_mode = rgb_mode;
389         out->alpha_mode = alpha_mode;
390 
391         return true;
392 }
393