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