blob: fda516c808978d6c9b7b1a927f487e720ac4d4d7 [file] [log] [blame]
/*
* © Copyright 2018 Alyssa Rosenzweig
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include <stdio.h>
#include "pan_blending.h"
#include "pan_context.h"
#include "gallium/auxiliary/util/u_blend.h"
#include "util/format/u_format.h"
/* Implements fixed-function blending on Midgard. */
/* Not all formats can be blended by fixed-function hardware */
bool
panfrost_can_fixed_blend(enum pipe_format format)
{
/* Fixed-function can handle sRGB */
format = util_format_linear(format);
switch (panfrost_pipe_format_table[format].hw) {
case MALI_RGB565:
case MALI_RGB5_A1_UNORM:
case MALI_RGB10_A2_UNORM:
case MALI_RGBA4_UNORM:
case MALI_R8_UNORM:
case MALI_RG8_UNORM:
case MALI_RGB8_UNORM:
case MALI_RGBA8_UNORM:
return true;
default:
return false;
}
}
/* Helper to find the uncomplemented Gallium blend factor corresponding to a
* complemented Gallium blend factor */
static int
complement_factor(int factor)
{
switch (factor) {
case PIPE_BLENDFACTOR_INV_SRC_COLOR:
return PIPE_BLENDFACTOR_SRC_COLOR;
case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
return PIPE_BLENDFACTOR_SRC_ALPHA;
case PIPE_BLENDFACTOR_INV_DST_ALPHA:
return PIPE_BLENDFACTOR_DST_ALPHA;
case PIPE_BLENDFACTOR_INV_DST_COLOR:
return PIPE_BLENDFACTOR_DST_COLOR;
case PIPE_BLENDFACTOR_INV_CONST_COLOR:
return PIPE_BLENDFACTOR_CONST_COLOR;
case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
return PIPE_BLENDFACTOR_CONST_ALPHA;
default:
return -1;
}
}
/* Helper to strip the complement from any Gallium blend factor */
static int
uncomplement_factor(int factor)
{
int complement = complement_factor(factor);
return (complement == -1) ? factor : complement;
}
/* Check if this is a special edge case blend factor, which may require the use
* of clip modifiers */
static bool
is_edge_blendfactor(unsigned factor)
{
return factor == PIPE_BLENDFACTOR_ONE || factor == PIPE_BLENDFACTOR_ZERO;
}
static bool
factor_is_supported(unsigned factor)
{
return factor != PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE &&
factor != PIPE_BLENDFACTOR_SRC1_COLOR &&
factor != PIPE_BLENDFACTOR_SRC1_ALPHA &&
factor != PIPE_BLENDFACTOR_INV_SRC1_COLOR &&
factor != PIPE_BLENDFACTOR_INV_SRC1_ALPHA;
}
static bool
can_use_fixed_function_blend(unsigned blend_func,
unsigned src_factor,
unsigned dest_factor)
{
if (blend_func != PIPE_BLEND_ADD &&
blend_func != PIPE_BLEND_SUBTRACT &&
blend_func != PIPE_BLEND_REVERSE_SUBTRACT)
return false;
if (!factor_is_supported(src_factor) ||
!factor_is_supported(dest_factor))
return false;
if (src_factor != dest_factor &&
src_factor != complement_factor(dest_factor) &&
complement_factor(src_factor) != dest_factor &&
!is_edge_blendfactor(src_factor) &&
!is_edge_blendfactor(dest_factor))
return false;
return true;
}
static void to_c_factor(unsigned factor, struct MALI_BLEND_FUNCTION *function)
{
if (complement_factor(factor) >= 0)
function->invert_c = true;
switch (uncomplement_factor(factor)) {
case PIPE_BLENDFACTOR_ONE:
case PIPE_BLENDFACTOR_ZERO:
function->invert_c = factor == PIPE_BLENDFACTOR_ONE;
function->c = MALI_BLEND_OPERAND_C_ZERO;
break;
case PIPE_BLENDFACTOR_SRC_ALPHA:
function->c = MALI_BLEND_OPERAND_C_SRC_ALPHA;
break;
case PIPE_BLENDFACTOR_DST_ALPHA:
function->c = MALI_BLEND_OPERAND_C_DEST_ALPHA;
break;
case PIPE_BLENDFACTOR_SRC_COLOR:
function->c = MALI_BLEND_OPERAND_C_SRC;
break;
case PIPE_BLENDFACTOR_DST_COLOR:
function->c = MALI_BLEND_OPERAND_C_DEST;
break;
case PIPE_BLENDFACTOR_CONST_COLOR:
case PIPE_BLENDFACTOR_CONST_ALPHA:
function->c = MALI_BLEND_OPERAND_C_CONSTANT;
break;
default:
unreachable("Invalid blend factor");
}
}
static bool
to_panfrost_function(unsigned blend_func,
unsigned src_factor,
unsigned dest_factor,
struct MALI_BLEND_FUNCTION *function)
{
if (!can_use_fixed_function_blend(blend_func, src_factor, dest_factor))
return false;
if (src_factor == PIPE_BLENDFACTOR_ZERO) {
function->a = MALI_BLEND_OPERAND_A_ZERO;
function->b = MALI_BLEND_OPERAND_B_DEST;
if (blend_func == PIPE_BLEND_SUBTRACT)
function->negate_b = true;
to_c_factor(dest_factor, function);
} else if (src_factor == PIPE_BLENDFACTOR_ONE) {
function->a = MALI_BLEND_OPERAND_A_SRC;
function->b = MALI_BLEND_OPERAND_B_DEST;
if (blend_func == PIPE_BLEND_SUBTRACT)
function->negate_b = true;
else if (blend_func == PIPE_BLEND_REVERSE_SUBTRACT)
function->negate_a = true;
to_c_factor(dest_factor, function);
} else if (dest_factor == PIPE_BLENDFACTOR_ZERO) {
function->a = MALI_BLEND_OPERAND_A_ZERO;
function->b = MALI_BLEND_OPERAND_B_SRC;
if (blend_func == PIPE_BLEND_REVERSE_SUBTRACT)
function->negate_b = true;
to_c_factor(src_factor, function);
} else if (dest_factor == PIPE_BLENDFACTOR_ONE) {
function->a = MALI_BLEND_OPERAND_A_DEST;
function->b = MALI_BLEND_OPERAND_B_SRC;
if (blend_func == PIPE_BLEND_SUBTRACT)
function->negate_a = true;
else if (blend_func == PIPE_BLEND_REVERSE_SUBTRACT)
function->negate_b = true;
to_c_factor(src_factor, function);
} else if (src_factor == dest_factor) {
function->a = MALI_BLEND_OPERAND_A_ZERO;
to_c_factor(src_factor, function);
switch (blend_func) {
case PIPE_BLEND_ADD:
function->b = MALI_BLEND_OPERAND_B_SRC_PLUS_DEST;
break;
case PIPE_BLEND_REVERSE_SUBTRACT:
function->negate_b = true;
/* fall-through */
case PIPE_BLEND_SUBTRACT:
function->b = MALI_BLEND_OPERAND_B_SRC_MINUS_DEST;
break;
default:
unreachable("Invalid blend function");
}
} else {
assert(src_factor == complement_factor(dest_factor) ||
complement_factor(src_factor) == dest_factor);
function->a = MALI_BLEND_OPERAND_A_DEST;
to_c_factor(src_factor, function);
switch (blend_func) {
case PIPE_BLEND_ADD:
function->b = MALI_BLEND_OPERAND_B_SRC_MINUS_DEST;
break;
case PIPE_BLEND_REVERSE_SUBTRACT:
function->b = MALI_BLEND_OPERAND_B_SRC_PLUS_DEST;
function->negate_b = true;
break;
case PIPE_BLEND_SUBTRACT:
function->b = MALI_BLEND_OPERAND_B_SRC_PLUS_DEST;
function->negate_a = true;
break;
}
}
return true;
}
/* We can upload a single constant for all of the factors. So, scan
* the factors for constants used to create a mask to check later. */
static unsigned
panfrost_constant_mask(unsigned *factors, unsigned num_factors)
{
unsigned mask = 0;
for (unsigned i = 0; i < num_factors; ++i) {
unsigned factor = uncomplement_factor(factors[i]);
if (factor == PIPE_BLENDFACTOR_CONST_COLOR)
mask |= 0b0111; /* RGB */
else if (factor == PIPE_BLENDFACTOR_CONST_ALPHA)
mask |= 0b1000; /* A */
}
return mask;
}
/* Create the descriptor for a fixed blend mode given the corresponding Gallium
* state, if possible. Return true and write out the blend descriptor into
* blend_equation. If it is not possible with the fixed function
* representating, return false to handle degenerate cases with a blend shader
*/
bool
panfrost_make_fixed_blend_mode(const struct pipe_rt_blend_state blend,
struct MALI_BLEND_EQUATION *equation,
unsigned *constant_mask)
{
/* If no blending is enabled, default back on `replace` mode */
if (!blend.blend_enable) {
equation->color_mask = blend.colormask;
equation->rgb.a = MALI_BLEND_OPERAND_A_SRC;
equation->rgb.b = MALI_BLEND_OPERAND_B_SRC;
equation->rgb.c = MALI_BLEND_OPERAND_C_ZERO;
equation->alpha.a = MALI_BLEND_OPERAND_A_SRC;
equation->alpha.b = MALI_BLEND_OPERAND_B_SRC;
equation->alpha.c = MALI_BLEND_OPERAND_C_ZERO;
return true;
}
/* At draw-time, we'll need to analyze the blend constant, so
* precompute a mask for it -- even if we don't end up able to use
* fixed-function blending */
unsigned factors[] = {
blend.rgb_src_factor, blend.rgb_dst_factor,
blend.alpha_src_factor, blend.alpha_dst_factor,
};
*constant_mask = panfrost_constant_mask(factors, ARRAY_SIZE(factors));
/* Try to compile the actual fixed-function blend */
if (!to_panfrost_function(blend.rgb_func, blend.rgb_src_factor,
blend.rgb_dst_factor,
&equation->rgb))
return false;
if (!to_panfrost_function(blend.alpha_func, blend.alpha_src_factor,
blend.alpha_dst_factor,
&equation->alpha))
return false;
equation->color_mask = blend.colormask;
return true;
}