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
28/*
29 * Implements fixed-function blending on Midgard.
30 *
31 * Midgard splits blending into a fixed-function fast path and a programmable
32 * slow path. The fixed function blending architecture is based on "dominant"
33 * blend factors. Blending is encoded separately (but identically) between RGB
34 * and alpha functions.
35 *
36 * Essentially, for a given blending operation, there is a single dominant
37 * factor. The following dominant factors are possible:
38 *
39 * 	- zero
40 * 	- source color
41 * 	- destination color
42 * 	- source alpha
43 * 	- destination alpha
44 * 	- constant float
45 *
46 * Further, a dominant factor's arithmetic compliment could be used. For
47 * instance, to encode GL_ONE_MINUS_SOURCE_ALPHA, the dominant factor would be
48 * MALI_DOMINANT_SRC_ALPHA with the complement_dominant bit set.
49 *
50 * A single constant float can be passed to the fixed-function hardware,
51 * allowing CONSTANT_ALPHA support. Further, if all components of the constant
52 * glBlendColor are identical, CONSTANT_COLOR can be implemented with the
53 * constant float mode. If the components differ, programmable blending is
54 * required.
55 *
56 * The nondominant factor can be either:
57 *
58 * 	- the same as the dominant factor (MALI_BLEND_NON_MIRROR)
59 * 	- zero (MALI_BLEND_NON_ZERO)
60 *
61 * Exactly one of the blend operation's source or destination can be used as
62 * the dominant factor; this is selected by the
63 * MALI_BLEND_DOM_SOURCE/DESTINATION flag.
64 *
65 * By default, all blending follows the standard OpenGL addition equation:
66 *
67 * 	out = source_value * source_factor + destination_value * destination_factor
68 *
69 * By setting the negate_source or negate_dest bits, other blend functions can
70 * be created. For instance, for SUBTRACT mode, set the "negate destination"
71 * flag, and similarly for REVERSE_SUBTRACT with "negate source".
72 *
73 * Finally, there is a "clip modifier" controlling the final blending
74 * behaviour, allowing for the following modes:
75 *
76 * 	- normal
77 * 	- force source factor to one (MALI_BLEND_MODE_SOURCE_ONE)
78 * 	- force destination factor to one (MALI_BLEND_MODE_DEST_ONE)
79 *
80 * The clipping flags can be used to encode blend modes where the nondominant
81 * factor is ONE.
82 *
83 * As an example putting it all together, to encode the following blend state:
84 *
85 * 	glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
86 * 	glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ONE);
87 *
88 * We need the following configuration:
89 *
90 * 	- negate source (for REVERSE_SUBTRACT)
91 * 	- dominant factor "source alpha"
92 * 		- compliment dominant
93 * 		- source dominant
94 * 	- force destination to ONE
95 *
96 * The following routines implement this fixed function blending encoding
97 */
98
99/* Helper to find the uncomplemented Gallium blend factor corresponding to a
100 * complemented Gallium blend factor */
101
102static int
103complement_factor(int factor)
104{
105        switch (factor) {
106        case PIPE_BLENDFACTOR_INV_SRC_COLOR:
107                return PIPE_BLENDFACTOR_SRC_COLOR;
108
109        case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
110                return PIPE_BLENDFACTOR_SRC_ALPHA;
111
112        case PIPE_BLENDFACTOR_INV_DST_ALPHA:
113                return PIPE_BLENDFACTOR_DST_ALPHA;
114
115        case PIPE_BLENDFACTOR_INV_DST_COLOR:
116                return PIPE_BLENDFACTOR_DST_COLOR;
117
118        case PIPE_BLENDFACTOR_INV_CONST_COLOR:
119                return PIPE_BLENDFACTOR_CONST_COLOR;
120
121        case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
122                return PIPE_BLENDFACTOR_CONST_ALPHA;
123
124        default:
125                return -1;
126        }
127}
128
129/* Helper to strip the complement from any Gallium blend factor */
130
131static int
132uncomplement_factor(int factor)
133{
134        int complement = complement_factor(factor);
135        return (complement == -1) ? factor : complement;
136}
137
138
139/* Attempt to find the dominant factor given a particular factor, complementing
140 * as necessary */
141
142static bool
143panfrost_make_dominant_factor(unsigned src_factor, enum mali_dominant_factor *factor, bool *invert)
144{
145        switch (src_factor) {
146        case PIPE_BLENDFACTOR_SRC_COLOR:
147        case PIPE_BLENDFACTOR_INV_SRC_COLOR:
148                *factor = MALI_DOMINANT_SRC_COLOR;
149                break;
150
151        case PIPE_BLENDFACTOR_SRC_ALPHA:
152        case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
153                *factor = MALI_DOMINANT_SRC_ALPHA;
154                break;
155
156        case PIPE_BLENDFACTOR_DST_COLOR:
157        case PIPE_BLENDFACTOR_INV_DST_COLOR:
158                *factor = MALI_DOMINANT_DST_COLOR;
159                break;
160
161        case PIPE_BLENDFACTOR_DST_ALPHA:
162        case PIPE_BLENDFACTOR_INV_DST_ALPHA:
163                *factor = MALI_DOMINANT_DST_ALPHA;
164                break;
165
166        case PIPE_BLENDFACTOR_ONE:
167        case PIPE_BLENDFACTOR_ZERO:
168                *factor = MALI_DOMINANT_ZERO;
169                break;
170
171        case PIPE_BLENDFACTOR_CONST_ALPHA:
172        case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
173        case PIPE_BLENDFACTOR_CONST_COLOR:
174        case PIPE_BLENDFACTOR_INV_CONST_COLOR:
175                *factor = MALI_DOMINANT_CONSTANT;
176                break;
177
178        default:
179                /* Fancy blend modes not supported */
180                return false;
181        }
182
183        /* Set invert flags */
184
185        switch (src_factor) {
186        case PIPE_BLENDFACTOR_ONE:
187        case PIPE_BLENDFACTOR_INV_SRC_COLOR:
188        case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
189        case PIPE_BLENDFACTOR_INV_DST_ALPHA:
190        case PIPE_BLENDFACTOR_INV_DST_COLOR:
191        case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
192        case PIPE_BLENDFACTOR_INV_CONST_COLOR:
193        case PIPE_BLENDFACTOR_INV_SRC1_COLOR:
194        case PIPE_BLENDFACTOR_INV_SRC1_ALPHA:
195                *invert = true;
196
197        default:
198                break;
199        }
200
201        return true;
202}
203
204/* Check if this is a special edge case blend factor, which may require the use
205 * of clip modifiers */
206
207static bool
208is_edge_blendfactor(unsigned factor)
209{
210        return factor == PIPE_BLENDFACTOR_ONE || factor == PIPE_BLENDFACTOR_ZERO;
211}
212
213/* Perform the actual fixed function encoding. Encode the function with negate
214 * bits. Check for various cases to work out the dominant/nondominant split and
215 * accompanying flags. */
216
217static bool
218panfrost_make_fixed_blend_part(unsigned func, unsigned src_factor, unsigned dst_factor, unsigned *out)
219{
220        struct mali_blend_mode part = { 0 };
221
222        /* Make sure that the blend function is representible with negate flags */
223
224        if (func == PIPE_BLEND_ADD) {
225                /* Default, no modifiers needed */
226        } else if (func == PIPE_BLEND_SUBTRACT)
227                part.negate_dest = true;
228        else if (func == PIPE_BLEND_REVERSE_SUBTRACT)
229                part.negate_source = true;
230        else
231                return false;
232
233        part.clip_modifier = MALI_BLEND_MOD_NORMAL;
234
235        /* Decide which is dominant, source or destination. If one is an edge
236         * case, use the other as a factor. If they're the same, it doesn't
237         * matter; we just mirror. If they're different non-edge-cases, you
238         * need a blend shader (don't do that). */
239
240        if (is_edge_blendfactor(dst_factor)) {
241                part.dominant = MALI_BLEND_DOM_SOURCE;
242                part.nondominant_mode = MALI_BLEND_NON_ZERO;
243
244                if (dst_factor == PIPE_BLENDFACTOR_ONE)
245                        part.clip_modifier = MALI_BLEND_MOD_DEST_ONE;
246        } else if (is_edge_blendfactor(src_factor)) {
247                part.dominant = MALI_BLEND_DOM_DESTINATION;
248                part.nondominant_mode = MALI_BLEND_NON_ZERO;
249
250                if (src_factor == PIPE_BLENDFACTOR_ONE)
251                        part.clip_modifier = MALI_BLEND_MOD_SOURCE_ONE;
252
253        } else if (src_factor == dst_factor) {
254                part.dominant = MALI_BLEND_DOM_DESTINATION; /* Ought to be an arbitrary choice, but we need to set destination for some reason? Align with the blob until we understand more */
255                part.nondominant_mode = MALI_BLEND_NON_MIRROR;
256        } else if (src_factor == complement_factor(dst_factor)) {
257                /* TODO: How does this work exactly? */
258                part.dominant = MALI_BLEND_DOM_SOURCE;
259                part.nondominant_mode = MALI_BLEND_NON_MIRROR;
260                part.clip_modifier = MALI_BLEND_MOD_DEST_ONE;
261        } else if (dst_factor == complement_factor(src_factor)) {
262                part.dominant = MALI_BLEND_DOM_SOURCE;
263                part.nondominant_mode = MALI_BLEND_NON_MIRROR;
264                part.clip_modifier = /*MALI_BLEND_MOD_SOURCE_ONE*/MALI_BLEND_MOD_DEST_ONE; /* Which modifier should it be? */
265        } else {
266                printf("Failed to find dominant factor?\n");
267                return false;
268        }
269
270        unsigned in_dominant_factor =
271                part.dominant == MALI_BLEND_DOM_SOURCE ? src_factor : dst_factor;
272
273        if (part.clip_modifier == MALI_BLEND_MOD_NORMAL && in_dominant_factor == PIPE_BLENDFACTOR_ONE) {
274                part.clip_modifier = part.dominant == MALI_BLEND_DOM_SOURCE ? MALI_BLEND_MOD_SOURCE_ONE : MALI_BLEND_MOD_DEST_ONE;
275                in_dominant_factor = PIPE_BLENDFACTOR_ZERO;
276        }
277
278        bool invert_dominant = false;
279        enum mali_dominant_factor dominant_factor;
280
281        if (!panfrost_make_dominant_factor(in_dominant_factor, &dominant_factor, &invert_dominant))
282                return false;
283
284        part.dominant_factor = dominant_factor;
285        part.complement_dominant = invert_dominant;
286
287        /* Write out mode */
288        memcpy(out, &part, sizeof(part));
289
290        return true;
291}
292
293/* We can upload a single constant for all of the factors. So, scan the factors
294 * for constants used, and scan the constants for the constants used. If there
295 * is a single unique constant, output that. If there are multiple,
296 * fixed-function operation breaks down. */
297
298static bool
299panfrost_make_constant(unsigned *factors, unsigned num_factors, const struct pipe_blend_color *blend_color, void *out)
300{
301        /* Color components used */
302        bool cc[4] = { false };
303
304        for (unsigned i = 0; i < num_factors; ++i) {
305                unsigned factor = uncomplement_factor(factors[i]);
306
307                if (factor == PIPE_BLENDFACTOR_CONST_COLOR)
308                        cc[0] = cc[1] = cc[2] = true;
309                else if (factor == PIPE_BLENDFACTOR_CONST_ALPHA)
310                        cc[3] = true;
311        }
312
313        /* Find the actual constant associated with the components used*/
314
315        float constant = 0.0;
316        bool has_constant = false;
317
318        for (unsigned i = 0; i < 4; ++i) {
319                /* If the component is unused, nothing to do */
320                if (!cc[i]) continue;
321
322                float value = blend_color->color[i];
323
324                /* Either there's a second constant, in which case we fail, or
325                 * there's no constant / a first constant, in which case we use
326                 * that constant */
327
328                if (has_constant && constant != value) {
329                        return false;
330                } else {
331                        has_constant = true;
332                        constant = value;
333                }
334        }
335
336        /* We have the constant -- success! */
337
338        memcpy(out, &constant, sizeof(float));
339        return true;
340}
341
342/* Create the descriptor for a fixed blend mode given the corresponding Gallium
343 * state, if possible. Return true and write out the blend descriptor into
344 * blend_equation. If it is not possible with the fixed function
345 * representating, return false to handle degenerate cases with a blend shader
346 */
347
348static const struct pipe_rt_blend_state default_blend = {
349        .blend_enable = 1,
350
351        .rgb_func = PIPE_BLEND_ADD,
352        .rgb_src_factor = PIPE_BLENDFACTOR_ONE,
353        .rgb_dst_factor = PIPE_BLENDFACTOR_ZERO,
354
355        .alpha_func = PIPE_BLEND_ADD,
356        .alpha_src_factor = PIPE_BLENDFACTOR_ONE,
357        .alpha_dst_factor = PIPE_BLENDFACTOR_ZERO,
358
359        .colormask = PIPE_MASK_RGBA
360};
361
362bool
363panfrost_make_fixed_blend_mode(const struct pipe_rt_blend_state *blend, struct mali_blend_equation *out, unsigned colormask, const struct pipe_blend_color *blend_color)
364{
365        /* If no blending is enabled, default back on `replace` mode */
366
367        if (!blend->blend_enable)
368                return panfrost_make_fixed_blend_mode(&default_blend, out, colormask, blend_color);
369
370        /* We have room only for a single float32 constant between the four
371         * components. If we need more, spill to the programmable pipeline. */
372
373        unsigned factors[] = {
374                blend->rgb_src_factor, blend->rgb_dst_factor,
375                blend->alpha_src_factor, blend->alpha_dst_factor,
376        };
377
378        if (!panfrost_make_constant(factors, ARRAY_SIZE(factors), blend_color, &out->constant))
379                return false;
380
381        unsigned rgb_mode = 0;
382        unsigned alpha_mode = 0;
383
384        if (!panfrost_make_fixed_blend_part(
385                                blend->rgb_func, blend->rgb_src_factor, blend->rgb_dst_factor,
386                                &rgb_mode))
387                return false;
388
389        if (!panfrost_make_fixed_blend_part(
390                                blend->alpha_func, blend->alpha_src_factor, blend->alpha_dst_factor,
391                                &alpha_mode))
392                return false;
393
394        out->rgb_mode = rgb_mode;
395        out->alpha_mode = alpha_mode;
396
397        /* Gallium and Mali represent colour masks identically. XXX: Static assert for future proof */
398        out->color_mask = colormask;
399
400        return true;
401}
402