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