panfrost: Check in sources for command stream
[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
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
102 static int
103 complement_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
131 static int
132 uncomplement_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
142 static bool
143 panfrost_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
207 static bool
208 is_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
217 static bool
218 panfrost_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
298 static bool
299 panfrost_make_constant(unsigned *factors, unsigned num_factors, const struct pipe_blend_color *blend_color, float *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 *out = constant;
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
348 static 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
362 bool
363 panfrost_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 }