2 * © Copyright 2018 Alyssa Rosenzweig
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:
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
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
26 #include "pan_blending.h"
27 #include "pan_context.h"
30 * Implements fixed-function blending on Midgard.
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.
37 * Essentially, for a given blending operation, there is a single dominant
38 * factor. The following dominant factors are possible:
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.
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
57 * The nondominant factor can be either:
59 * - the same as the dominant factor (MALI_BLEND_NON_MIRROR)
60 * - zero (MALI_BLEND_NON_ZERO)
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.
66 * By default, all blending follows the standard OpenGL addition equation:
68 * out = source_value * source_factor + destination_value * destination_factor
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".
74 * Finally, there is a "clip modifier" controlling the final blending
75 * behaviour, allowing for the following modes:
78 * - force source factor to one (MALI_BLEND_MODE_SOURCE_ONE)
79 * - force destination factor to one (MALI_BLEND_MODE_DEST_ONE)
81 * The clipping flags can be used to encode blend modes where the nondominant
84 * As an example putting it all together, to encode the following blend state:
86 * glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
87 * glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ONE);
89 * We need the following configuration:
91 * - negate source (for REVERSE_SUBTRACT)
92 * - dominant factor "source alpha"
93 * - compliment dominant
95 * - force destination to ONE
97 * The following routines implement this fixed function blending encoding
100 /* Helper to find the uncomplemented Gallium blend factor corresponding to a
101 * complemented Gallium blend factor */
104 complement_factor(int factor
)
107 case PIPE_BLENDFACTOR_INV_SRC_COLOR
:
108 return PIPE_BLENDFACTOR_SRC_COLOR
;
110 case PIPE_BLENDFACTOR_INV_SRC_ALPHA
:
111 return PIPE_BLENDFACTOR_SRC_ALPHA
;
113 case PIPE_BLENDFACTOR_INV_DST_ALPHA
:
114 return PIPE_BLENDFACTOR_DST_ALPHA
;
116 case PIPE_BLENDFACTOR_INV_DST_COLOR
:
117 return PIPE_BLENDFACTOR_DST_COLOR
;
119 case PIPE_BLENDFACTOR_INV_CONST_COLOR
:
120 return PIPE_BLENDFACTOR_CONST_COLOR
;
122 case PIPE_BLENDFACTOR_INV_CONST_ALPHA
:
123 return PIPE_BLENDFACTOR_CONST_ALPHA
;
130 /* Helper to strip the complement from any Gallium blend factor */
133 uncomplement_factor(int factor
)
135 int complement
= complement_factor(factor
);
136 return (complement
== -1) ? factor
: complement
;
140 /* Attempt to find the dominant factor given a particular factor, complementing
144 panfrost_make_dominant_factor(unsigned src_factor
, enum mali_dominant_factor
*factor
, bool *invert
)
146 switch (src_factor
) {
147 case PIPE_BLENDFACTOR_SRC_COLOR
:
148 case PIPE_BLENDFACTOR_INV_SRC_COLOR
:
149 *factor
= MALI_DOMINANT_SRC_COLOR
;
152 case PIPE_BLENDFACTOR_SRC_ALPHA
:
153 case PIPE_BLENDFACTOR_INV_SRC_ALPHA
:
154 *factor
= MALI_DOMINANT_SRC_ALPHA
;
157 case PIPE_BLENDFACTOR_DST_COLOR
:
158 case PIPE_BLENDFACTOR_INV_DST_COLOR
:
159 *factor
= MALI_DOMINANT_DST_COLOR
;
162 case PIPE_BLENDFACTOR_DST_ALPHA
:
163 case PIPE_BLENDFACTOR_INV_DST_ALPHA
:
164 *factor
= MALI_DOMINANT_DST_ALPHA
;
167 case PIPE_BLENDFACTOR_ONE
:
168 case PIPE_BLENDFACTOR_ZERO
:
169 *factor
= MALI_DOMINANT_ZERO
;
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
;
180 /* Fancy blend modes not supported */
184 /* Set invert flags */
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
:
205 /* Check if this is a special edge case blend factor, which may require the use
206 * of clip modifiers */
209 is_edge_blendfactor(unsigned factor
)
211 return factor
== PIPE_BLENDFACTOR_ONE
|| factor
== PIPE_BLENDFACTOR_ZERO
;
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. */
219 panfrost_make_fixed_blend_part(unsigned func
, unsigned src_factor
, unsigned dst_factor
, unsigned *out
)
221 struct mali_blend_mode part
= { 0 };
223 /* Make sure that the blend function is representible with negate flags */
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;
234 part
.clip_modifier
= MALI_BLEND_MOD_NORMAL
;
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). */
241 if (is_edge_blendfactor(dst_factor
)) {
242 part
.dominant
= MALI_BLEND_DOM_SOURCE
;
243 part
.nondominant_mode
= MALI_BLEND_NON_ZERO
;
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
;
251 if (src_factor
== PIPE_BLENDFACTOR_ONE
)
252 part
.clip_modifier
= MALI_BLEND_MOD_SOURCE_ONE
;
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? */
267 printf("Failed to find dominant factor?\n");
271 unsigned in_dominant_factor
=
272 part
.dominant
== MALI_BLEND_DOM_SOURCE
? src_factor
: dst_factor
;
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
;
279 bool invert_dominant
= false;
280 enum mali_dominant_factor dominant_factor
;
282 if (!panfrost_make_dominant_factor(in_dominant_factor
, &dominant_factor
, &invert_dominant
))
285 part
.dominant_factor
= dominant_factor
;
286 part
.complement_dominant
= invert_dominant
;
289 memcpy(out
, &part
, sizeof(part
));
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. */
300 panfrost_make_constant(unsigned *factors
, unsigned num_factors
, const struct pipe_blend_color
*blend_color
, void *out
)
302 /* Color components used */
303 bool cc
[4] = { false };
305 for (unsigned i
= 0; i
< num_factors
; ++i
) {
306 unsigned factor
= uncomplement_factor(factors
[i
]);
308 if (factor
== PIPE_BLENDFACTOR_CONST_COLOR
)
309 cc
[0] = cc
[1] = cc
[2] = true;
310 else if (factor
== PIPE_BLENDFACTOR_CONST_ALPHA
)
314 /* Find the actual constant associated with the components used*/
316 float constant
= 0.0;
317 bool has_constant
= false;
319 for (unsigned i
= 0; i
< 4; ++i
) {
320 /* If the component is unused, nothing to do */
321 if (!cc
[i
]) continue;
323 float value
= blend_color
->color
[i
];
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
329 if (has_constant
&& constant
!= value
) {
337 /* We have the constant -- success! */
339 memcpy(out
, &constant
, sizeof(float));
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
349 static const struct pipe_rt_blend_state default_blend
= {
352 .rgb_func
= PIPE_BLEND_ADD
,
353 .rgb_src_factor
= PIPE_BLENDFACTOR_ONE
,
354 .rgb_dst_factor
= PIPE_BLENDFACTOR_ZERO
,
356 .alpha_func
= PIPE_BLEND_ADD
,
357 .alpha_src_factor
= PIPE_BLENDFACTOR_ONE
,
358 .alpha_dst_factor
= PIPE_BLENDFACTOR_ZERO
,
360 .colormask
= PIPE_MASK_RGBA
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
)
366 struct mali_blend_equation
*out
= &so
->equation
;
368 /* If no blending is enabled, default back on `replace` mode */
370 if (!blend
->blend_enable
)
371 return panfrost_make_fixed_blend_mode(&default_blend
, so
, colormask
, blend_color
);
373 /* We have room only for a single float32 constant between the four
374 * components. If we need more, spill to the programmable pipeline. */
376 unsigned factors
[] = {
377 blend
->rgb_src_factor
, blend
->rgb_dst_factor
,
378 blend
->alpha_src_factor
, blend
->alpha_dst_factor
,
381 if (!panfrost_make_constant(factors
, ARRAY_SIZE(factors
), blend_color
, &so
->constant
))
384 unsigned rgb_mode
= 0;
385 unsigned alpha_mode
= 0;
387 if (!panfrost_make_fixed_blend_part(
388 blend
->rgb_func
, blend
->rgb_src_factor
, blend
->rgb_dst_factor
,
392 if (!panfrost_make_fixed_blend_part(
393 blend
->alpha_func
, blend
->alpha_src_factor
, blend
->alpha_dst_factor
,
397 out
->rgb_mode
= rgb_mode
;
398 out
->alpha_mode
= alpha_mode
;
400 /* Gallium and Mali represent colour masks identically. XXX: Static assert for future proof */
401 out
->color_mask
= colormask
;