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