panfrost: Route format through fixed-function blending
[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 #include "gallium/auxiliary/util/u_blend.h"
29 #include "util/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 static bool
105 panfrost_can_blend(enum pipe_format format)
106 {
107 /* Fixed-function can handle sRGB */
108 format = util_format_linear(format);
109
110 /* Decompose the format */
111 const struct util_format_description *desc =
112 util_format_description(format);
113
114 /* Any 8-bit unorm is supported */
115 if (util_format_is_unorm8(desc))
116 return true;
117
118 /* Certain special formats are, too */
119 switch (format) {
120 case PIPE_FORMAT_B5G6R5_UNORM:
121 case PIPE_FORMAT_B4G4R4A4_UNORM:
122 case PIPE_FORMAT_B5G5R5A1_UNORM:
123 case PIPE_FORMAT_R10G10B10A2_UNORM:
124 return true;
125 default:
126 return false;
127 }
128 }
129
130 /* Helper to find the uncomplemented Gallium blend factor corresponding to a
131 * complemented Gallium blend factor */
132
133 static int
134 complement_factor(int factor)
135 {
136 switch (factor) {
137 case PIPE_BLENDFACTOR_INV_SRC_COLOR:
138 return PIPE_BLENDFACTOR_SRC_COLOR;
139
140 case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
141 return PIPE_BLENDFACTOR_SRC_ALPHA;
142
143 case PIPE_BLENDFACTOR_INV_DST_ALPHA:
144 return PIPE_BLENDFACTOR_DST_ALPHA;
145
146 case PIPE_BLENDFACTOR_INV_DST_COLOR:
147 return PIPE_BLENDFACTOR_DST_COLOR;
148
149 case PIPE_BLENDFACTOR_INV_CONST_COLOR:
150 return PIPE_BLENDFACTOR_CONST_COLOR;
151
152 case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
153 return PIPE_BLENDFACTOR_CONST_ALPHA;
154
155 default:
156 return -1;
157 }
158 }
159
160 /* Helper to strip the complement from any Gallium blend factor */
161
162 static int
163 uncomplement_factor(int factor)
164 {
165 int complement = complement_factor(factor);
166 return (complement == -1) ? factor : complement;
167 }
168
169
170 /* Attempt to find the dominant factor given a particular factor, complementing
171 * as necessary */
172
173 static bool
174 panfrost_make_dominant_factor(unsigned src_factor, enum mali_dominant_factor *factor)
175 {
176 switch (src_factor) {
177 case PIPE_BLENDFACTOR_SRC_COLOR:
178 case PIPE_BLENDFACTOR_INV_SRC_COLOR:
179 *factor = MALI_DOMINANT_SRC_COLOR;
180 break;
181
182 case PIPE_BLENDFACTOR_SRC_ALPHA:
183 case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
184 *factor = MALI_DOMINANT_SRC_ALPHA;
185 break;
186
187 case PIPE_BLENDFACTOR_DST_COLOR:
188 case PIPE_BLENDFACTOR_INV_DST_COLOR:
189 *factor = MALI_DOMINANT_DST_COLOR;
190 break;
191
192 case PIPE_BLENDFACTOR_DST_ALPHA:
193 case PIPE_BLENDFACTOR_INV_DST_ALPHA:
194 *factor = MALI_DOMINANT_DST_ALPHA;
195 break;
196
197 case PIPE_BLENDFACTOR_ONE:
198 case PIPE_BLENDFACTOR_ZERO:
199 *factor = MALI_DOMINANT_ZERO;
200 break;
201
202 case PIPE_BLENDFACTOR_CONST_ALPHA:
203 case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
204 case PIPE_BLENDFACTOR_CONST_COLOR:
205 case PIPE_BLENDFACTOR_INV_CONST_COLOR:
206 *factor = MALI_DOMINANT_CONSTANT;
207 break;
208
209 default:
210 /* Fancy blend modes not supported */
211 return false;
212 }
213
214 return true;
215 }
216
217 /* Check if this is a special edge case blend factor, which may require the use
218 * of clip modifiers */
219
220 static bool
221 is_edge_blendfactor(unsigned factor)
222 {
223 return factor == PIPE_BLENDFACTOR_ONE || factor == PIPE_BLENDFACTOR_ZERO;
224 }
225
226 /* Perform the actual fixed function encoding. Encode the function with negate
227 * bits. Check for various cases to work out the dominant/nondominant split and
228 * accompanying flags. */
229
230 static bool
231 panfrost_make_fixed_blend_part(unsigned func, unsigned src_factor, unsigned dst_factor, unsigned *out)
232 {
233 struct mali_blend_mode part = { 0 };
234
235 /* Make sure that the blend function is representible */
236
237 switch (func) {
238 case PIPE_BLEND_ADD:
239 break;
240
241 /* TODO: Reenable subtraction modes when those fixed */
242 case PIPE_BLEND_SUBTRACT:
243 case PIPE_BLEND_REVERSE_SUBTRACT:
244 default:
245 return false;
246 }
247
248 part.clip_modifier = MALI_BLEND_MOD_NORMAL;
249
250 /* Decide which is dominant, source or destination. If one is an edge
251 * case, use the other as a factor. If they're the same, it doesn't
252 * matter; we just mirror. If they're different non-edge-cases, you
253 * need a blend shader (don't do that). */
254
255 if (is_edge_blendfactor(dst_factor)) {
256 part.dominant = MALI_BLEND_DOM_SOURCE;
257 part.nondominant_mode = MALI_BLEND_NON_ZERO;
258
259 if (dst_factor == PIPE_BLENDFACTOR_ONE)
260 part.clip_modifier = MALI_BLEND_MOD_DEST_ONE;
261 } else if (is_edge_blendfactor(src_factor)) {
262 part.dominant = MALI_BLEND_DOM_DESTINATION;
263 part.nondominant_mode = MALI_BLEND_NON_ZERO;
264
265 if (src_factor == PIPE_BLENDFACTOR_ONE)
266 part.clip_modifier = MALI_BLEND_MOD_SOURCE_ONE;
267 } else if (src_factor == dst_factor) {
268 /* XXX: Why? */
269 part.dominant = func == PIPE_BLEND_ADD ?
270 MALI_BLEND_DOM_DESTINATION : MALI_BLEND_DOM_SOURCE;
271
272 part.nondominant_mode = MALI_BLEND_NON_MIRROR;
273 } else if (src_factor == complement_factor(dst_factor)) {
274 /* TODO: How does this work exactly? */
275 part.dominant = MALI_BLEND_DOM_SOURCE;
276 part.nondominant_mode = MALI_BLEND_NON_MIRROR;
277 part.clip_modifier = MALI_BLEND_MOD_DEST_ONE;
278
279 /* The complement is handled by the clip modifier, don't set a
280 * complement flag */
281
282 dst_factor = src_factor;
283 } else if (dst_factor == complement_factor(src_factor)) {
284 part.dominant = MALI_BLEND_DOM_SOURCE;
285 part.nondominant_mode = MALI_BLEND_NON_MIRROR;
286 part.clip_modifier = MALI_BLEND_MOD_SOURCE_ONE;
287
288 src_factor = dst_factor;
289 } else {
290 return false;
291 }
292
293 unsigned in_dominant_factor =
294 part.dominant == MALI_BLEND_DOM_SOURCE ? src_factor : dst_factor;
295
296 if (part.clip_modifier == MALI_BLEND_MOD_NORMAL && in_dominant_factor == PIPE_BLENDFACTOR_ONE) {
297 part.clip_modifier = part.dominant == MALI_BLEND_DOM_SOURCE ? MALI_BLEND_MOD_SOURCE_ONE : MALI_BLEND_MOD_DEST_ONE;
298 in_dominant_factor = PIPE_BLENDFACTOR_ZERO;
299 }
300
301 enum mali_dominant_factor dominant_factor;
302
303 if (!panfrost_make_dominant_factor(in_dominant_factor, &dominant_factor))
304 return false;
305
306 part.dominant_factor = dominant_factor;
307 part.complement_dominant = util_blend_factor_is_inverted(in_dominant_factor);
308
309 /* It's not clear what this does, but fixes some ADD blending tests.
310 * More research is needed XXX */
311
312 if ((part.clip_modifier == MALI_BLEND_MOD_SOURCE_ONE) && (part.dominant == MALI_BLEND_DOM_SOURCE))
313 part.negate_dest = true;
314
315 /* Write out mode */
316 memcpy(out, &part, sizeof(part));
317
318 return true;
319 }
320
321 /* We can upload a single constant for all of the factors. So, scan the factors
322 * for constants used, and scan the constants for the constants used. If there
323 * is a single unique constant, output that. If there are multiple,
324 * fixed-function operation breaks down. */
325
326 static bool
327 panfrost_make_constant(unsigned *factors, unsigned num_factors, const struct pipe_blend_color *blend_color, void *out)
328 {
329 /* Color components used */
330 bool cc[4] = { false };
331
332 for (unsigned i = 0; i < num_factors; ++i) {
333 unsigned factor = uncomplement_factor(factors[i]);
334
335 if (factor == PIPE_BLENDFACTOR_CONST_COLOR)
336 cc[0] = cc[1] = cc[2] = true;
337 else if (factor == PIPE_BLENDFACTOR_CONST_ALPHA)
338 cc[3] = true;
339 }
340
341 /* Find the actual constant associated with the components used*/
342
343 float constant = 0.0;
344 bool has_constant = false;
345
346 for (unsigned i = 0; i < 4; ++i) {
347 /* If the component is unused, nothing to do */
348 if (!cc[i]) continue;
349
350 float value = blend_color->color[i];
351
352 /* Either there's a second constant, in which case we fail, or
353 * there's no constant / a first constant, in which case we use
354 * that constant */
355
356 if (has_constant && constant != value) {
357 return false;
358 } else {
359 has_constant = true;
360 constant = value;
361 }
362 }
363
364 /* We have the constant -- success! */
365
366 memcpy(out, &constant, sizeof(float));
367 return true;
368 }
369
370 /* Create the descriptor for a fixed blend mode given the corresponding Gallium
371 * state, if possible. Return true and write out the blend descriptor into
372 * blend_equation. If it is not possible with the fixed function
373 * representating, return false to handle degenerate cases with a blend shader
374 */
375
376 bool
377 panfrost_make_fixed_blend_mode(
378 const struct pipe_rt_blend_state *blend,
379 struct panfrost_blend_state *so,
380 unsigned colormask,
381 const struct pipe_blend_color *blend_color,
382 enum pipe_format format)
383 {
384 struct mali_blend_equation *out = &so->equation;
385
386 /* Check if the format supports fixed-function blending at all */
387
388 if (!panfrost_can_blend(format))
389 return false;
390
391 /* Gallium and Mali represent colour masks identically. XXX: Static assert for future proof */
392 out->color_mask = colormask;
393
394 /* If no blending is enabled, default back on `replace` mode */
395
396 if (!blend->blend_enable) {
397 out->rgb_mode = 0x122;
398 out->alpha_mode = 0x122;
399 return true;
400 }
401
402 /* We have room only for a single float32 constant between the four
403 * components. If we need more, spill to the programmable pipeline. */
404
405 unsigned factors[] = {
406 blend->rgb_src_factor, blend->rgb_dst_factor,
407 blend->alpha_src_factor, blend->alpha_dst_factor,
408 };
409
410 if (!panfrost_make_constant(factors, ARRAY_SIZE(factors), blend_color, &so->constant))
411 return false;
412
413 unsigned rgb_mode = 0;
414 unsigned alpha_mode = 0;
415
416 if (!panfrost_make_fixed_blend_part(
417 blend->rgb_func, blend->rgb_src_factor, blend->rgb_dst_factor,
418 &rgb_mode))
419 return false;
420
421 if (!panfrost_make_fixed_blend_part(
422 blend->alpha_func, blend->alpha_src_factor, blend->alpha_dst_factor,
423 &alpha_mode))
424 return false;
425
426 out->rgb_mode = rgb_mode;
427 out->alpha_mode = alpha_mode;
428
429 return true;
430 }