mesa/src/panfrost/lib/pan_blend.c

853 lines
34 KiB
C

/*
* Copyright (C) 2018 Alyssa Rosenzweig
* Copyright (C) 2019-2021 Collabora, Ltd.
*
* 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 "pan_blend.h"
#ifdef PAN_ARCH
#include "pan_shader.h"
#endif
#include "pan_texture.h"
#include "panfrost/util/pan_lower_framebuffer.h"
#include "util/format/u_format.h"
#include "compiler/nir/nir.h"
#include "compiler/nir/nir_builder.h"
#include "compiler/nir/nir_conversion_builder.h"
#include "compiler/nir/nir_lower_blend.h"
#ifndef PAN_ARCH
/* Fixed function blending */
static bool
factor_is_supported(enum blend_factor factor)
{
return factor != BLEND_FACTOR_SRC_ALPHA_SATURATE &&
factor != BLEND_FACTOR_SRC1_COLOR &&
factor != BLEND_FACTOR_SRC1_ALPHA;
}
/* OpenGL allows encoding (src*dest + dest*src) which is incompatiblle with
* Midgard style blending since there are two multiplies. However, it may be
* factored as 2*src*dest = dest*(2*src), which can be encoded on Bifrost as 0
* + dest * (2*src) wih the new source_2 value of C. Detect this case. */
static bool
is_2srcdest(enum blend_func blend_func,
enum blend_factor src_factor,
bool invert_src,
enum blend_factor dest_factor,
bool invert_dest,
bool is_alpha)
{
return (blend_func == BLEND_FUNC_ADD) &&
((src_factor == BLEND_FACTOR_DST_COLOR) ||
((src_factor == BLEND_FACTOR_DST_ALPHA) && is_alpha)) &&
((dest_factor == BLEND_FACTOR_SRC_COLOR) ||
((dest_factor == BLEND_FACTOR_SRC_ALPHA) && is_alpha)) &&
!invert_src && !invert_dest;
}
static bool
can_fixed_function_equation(enum blend_func blend_func,
enum blend_factor src_factor,
bool invert_src,
enum blend_factor dest_factor,
bool invert_dest,
bool is_alpha,
bool supports_2src)
{
if (is_2srcdest(blend_func, src_factor, invert_src,
dest_factor, invert_dest, is_alpha)) {
return supports_2src;
}
if (blend_func != BLEND_FUNC_ADD &&
blend_func != BLEND_FUNC_SUBTRACT &&
blend_func != BLEND_FUNC_REVERSE_SUBTRACT)
return false;
if (!factor_is_supported(src_factor) ||
!factor_is_supported(dest_factor))
return false;
if (src_factor != dest_factor &&
src_factor != BLEND_FACTOR_ZERO &&
dest_factor != BLEND_FACTOR_ZERO)
return false;
return true;
}
static unsigned
blend_factor_constant_mask(enum blend_factor factor)
{
if (factor == BLEND_FACTOR_CONSTANT_COLOR)
return 0b0111; /* RGB */
else if (factor == BLEND_FACTOR_CONSTANT_ALPHA)
return 0b1000; /* A */
else
return 0b0000; /* - */
}
unsigned
pan_blend_constant_mask(const struct pan_blend_equation eq)
{
return blend_factor_constant_mask(eq.rgb_src_factor) |
blend_factor_constant_mask(eq.rgb_dst_factor) |
blend_factor_constant_mask(eq.alpha_src_factor) |
blend_factor_constant_mask(eq.alpha_dst_factor);
}
/* Only "homogenous" (scalar or vector with all components equal) constants are
* valid for fixed-function, so check for this condition */
bool
pan_blend_is_homogenous_constant(unsigned mask, const float *constants)
{
float constant = pan_blend_get_constant(mask, constants);
u_foreach_bit(i, mask) {
if (constants[i] != constant)
return false;
}
return true;
}
/* Determines if an equation can run in fixed function */
bool
pan_blend_can_fixed_function(const struct pan_blend_equation equation,
bool supports_2src)
{
return !equation.blend_enable ||
(can_fixed_function_equation(equation.rgb_func,
equation.rgb_src_factor,
equation.rgb_invert_src_factor,
equation.rgb_dst_factor,
equation.rgb_invert_dst_factor,
false, supports_2src) &&
can_fixed_function_equation(equation.alpha_func,
equation.alpha_src_factor,
equation.alpha_invert_src_factor,
equation.alpha_dst_factor,
equation.alpha_invert_dst_factor,
true, supports_2src));
}
static enum mali_blend_operand_c
to_c_factor(enum blend_factor factor)
{
switch (factor) {
case BLEND_FACTOR_ZERO:
return MALI_BLEND_OPERAND_C_ZERO;
case BLEND_FACTOR_SRC_ALPHA:
return MALI_BLEND_OPERAND_C_SRC_ALPHA;
case BLEND_FACTOR_DST_ALPHA:
return MALI_BLEND_OPERAND_C_DEST_ALPHA;
case BLEND_FACTOR_SRC_COLOR:
return MALI_BLEND_OPERAND_C_SRC;
case BLEND_FACTOR_DST_COLOR:
return MALI_BLEND_OPERAND_C_DEST;
case BLEND_FACTOR_CONSTANT_COLOR:
case BLEND_FACTOR_CONSTANT_ALPHA:
return MALI_BLEND_OPERAND_C_CONSTANT;
default:
unreachable("Unsupported blend factor");
}
}
static void
to_panfrost_function(enum blend_func blend_func,
enum blend_factor src_factor,
bool invert_src,
enum blend_factor dest_factor,
bool invert_dest,
bool is_alpha,
struct MALI_BLEND_FUNCTION *function)
{
assert(can_fixed_function_equation(blend_func, src_factor, invert_src,
dest_factor, invert_dest, is_alpha, true));
if (src_factor == BLEND_FACTOR_ZERO && !invert_src) {
function->a = MALI_BLEND_OPERAND_A_ZERO;
function->b = MALI_BLEND_OPERAND_B_DEST;
if (blend_func == BLEND_FUNC_SUBTRACT)
function->negate_b = true;
function->invert_c = invert_dest;
function->c = to_c_factor(dest_factor);
} else if (src_factor == BLEND_FACTOR_ZERO && invert_src) {
function->a = MALI_BLEND_OPERAND_A_SRC;
function->b = MALI_BLEND_OPERAND_B_DEST;
if (blend_func == BLEND_FUNC_SUBTRACT)
function->negate_b = true;
else if (blend_func == BLEND_FUNC_REVERSE_SUBTRACT)
function->negate_a = true;
function->invert_c = invert_dest;
function->c = to_c_factor(dest_factor);
} else if (dest_factor == BLEND_FACTOR_ZERO && !invert_dest) {
function->a = MALI_BLEND_OPERAND_A_ZERO;
function->b = MALI_BLEND_OPERAND_B_SRC;
if (blend_func == BLEND_FUNC_REVERSE_SUBTRACT)
function->negate_b = true;
function->invert_c = invert_src;
function->c = to_c_factor(src_factor);
} else if (dest_factor == BLEND_FACTOR_ZERO && invert_dest) {
function->a = MALI_BLEND_OPERAND_A_DEST;
function->b = MALI_BLEND_OPERAND_B_SRC;
if (blend_func == BLEND_FUNC_SUBTRACT)
function->negate_a = true;
else if (blend_func == BLEND_FUNC_REVERSE_SUBTRACT)
function->negate_b = true;
function->invert_c = invert_src;
function->c = to_c_factor(src_factor);
} else if (src_factor == dest_factor && invert_src == invert_dest) {
function->a = MALI_BLEND_OPERAND_A_ZERO;
function->invert_c = invert_src;
function->c = to_c_factor(src_factor);
switch (blend_func) {
case BLEND_FUNC_ADD:
function->b = MALI_BLEND_OPERAND_B_SRC_PLUS_DEST;
break;
case BLEND_FUNC_REVERSE_SUBTRACT:
function->negate_b = true;
FALLTHROUGH;
case BLEND_FUNC_SUBTRACT:
function->b = MALI_BLEND_OPERAND_B_SRC_MINUS_DEST;
break;
default:
unreachable("Invalid blend function");
}
} else if (is_2srcdest(blend_func, src_factor, invert_src, dest_factor,
invert_dest, is_alpha)) {
/* src*dest + dest*src = 2*src*dest = 0 + dest*(2*src) */
function->a = MALI_BLEND_OPERAND_A_ZERO;
function->b = MALI_BLEND_OPERAND_B_DEST;
function->c = MALI_BLEND_OPERAND_C_SRC_X_2;
} else {
assert(src_factor == dest_factor && invert_src != invert_dest);
function->a = MALI_BLEND_OPERAND_A_DEST;
function->invert_c = invert_src;
function->c = to_c_factor(src_factor);
switch (blend_func) {
case BLEND_FUNC_ADD:
function->b = MALI_BLEND_OPERAND_B_SRC_MINUS_DEST;
break;
case BLEND_FUNC_REVERSE_SUBTRACT:
function->b = MALI_BLEND_OPERAND_B_SRC_PLUS_DEST;
function->negate_b = true;
break;
case BLEND_FUNC_SUBTRACT:
function->b = MALI_BLEND_OPERAND_B_SRC_PLUS_DEST;
function->negate_a = true;
break;
default:
unreachable("Invalid blend function\n");
}
}
}
bool
pan_blend_is_opaque(const struct pan_blend_equation equation)
{
/* If a channel is masked out, we can't use opaque mode even if
* blending is disabled, since we need a tilebuffer read in there */
if (equation.color_mask != 0xF)
return false;
/* With nothing masked out, disabled bledning is opaque */
if (!equation.blend_enable)
return true;
/* Also detect open-coded opaque blending */
return equation.rgb_src_factor == BLEND_FACTOR_ZERO &&
equation.rgb_invert_src_factor &&
equation.rgb_dst_factor == BLEND_FACTOR_ZERO &&
!equation.rgb_invert_dst_factor &&
(equation.rgb_func == BLEND_FUNC_ADD ||
equation.rgb_func == BLEND_FUNC_SUBTRACT) &&
equation.alpha_src_factor == BLEND_FACTOR_ZERO &&
equation.alpha_invert_src_factor &&
equation.alpha_dst_factor == BLEND_FACTOR_ZERO &&
!equation.alpha_invert_dst_factor &&
(equation.alpha_func == BLEND_FUNC_ADD ||
equation.alpha_func == BLEND_FUNC_SUBTRACT);
}
/* Check if (factor, invert) represents a constant value of val, assuming
* src_alpha is the given constant.
*/
static inline bool
is_factor_01(unsigned factor, bool invert, unsigned val, unsigned srca)
{
assert(val == 0 || val == 1);
assert(srca == 0 || srca == 1);
return ((invert ^ !val) && factor == BLEND_FACTOR_ZERO) ||
((invert ^ srca ^ !val) && factor == BLEND_FACTOR_SRC_ALPHA);
}
/* Returns if src alpha = 0 implies the blended colour equals the destination
* colour. Suppose source alpha = 0 and consider cases.
*
* Additive blending: Equivalent to D = S * f_s + D * f_d for all D and all S
* with S_a = 0, for each component. For the alpha component (if it unmasked),
* we have S_a = 0 so this reduces to D = D * f_d <===> f_d = 1. For RGB
* components (if unmasked), we need f_s = 0 and f_d = 1.
*
* Subtractive blending: Fails in general (D = S * f_S - D * f_D). We
* would need f_S = 0 and f_D = -1, which is not valid in the APIs.
*
* Reverse subtractive blending (D = D * f_D - S * f_S), we need f_D = 1
* and f_S = 0 up to masking. This is the same as additive blending.
*
* Min/max: Fails in general on the RGB components.
*/
bool
pan_blend_alpha_zero_nop(const struct pan_blend_equation eq)
{
if (eq.rgb_func != BLEND_FUNC_ADD &&
eq.rgb_func != BLEND_FUNC_REVERSE_SUBTRACT)
return false;
if (eq.color_mask & 0x8) {
if (!is_factor_01(eq.alpha_dst_factor, eq.alpha_invert_dst_factor, 1, 0))
return false;
}
if (eq.color_mask & 0x7) {
if (!is_factor_01(eq.rgb_dst_factor, eq.rgb_invert_dst_factor, 1, 0))
return false;
if (!is_factor_01(eq.rgb_src_factor, eq.rgb_invert_src_factor, 0, 0))
return false;
}
return true;
}
/* Returns if src alpha = 1 implies the blended colour equals the source
* colour. Suppose source alpha = 1 and consider cases.
*
* Additive blending: S = S * f_s + D * f_d. We need f_s = 1 and f_d = 0.
*
* Subtractive blending: S = S * f_s - D * f_d. Same as additive blending.
*
* Reverse subtractive blending: S = D * f_d - S * f_s. Fails in general since
* it would require f_s = -1, which is not valid in the APIs.
*
* Min/max: Fails in general on the RGB components.
*
* Note if any component is masked, we can't use a store.
*/
bool
pan_blend_alpha_one_store(const struct pan_blend_equation eq)
{
if (eq.rgb_func != BLEND_FUNC_ADD &&
eq.rgb_func != BLEND_FUNC_SUBTRACT)
return false;
if (eq.color_mask != 0xf)
return false;
return is_factor_01(eq.rgb_src_factor, eq.rgb_invert_src_factor, 1, 1) &&
is_factor_01(eq.alpha_src_factor, eq.alpha_invert_src_factor, 1, 1) &&
is_factor_01(eq.rgb_dst_factor, eq.rgb_invert_dst_factor, 0, 1) &&
is_factor_01(eq.alpha_dst_factor, eq.alpha_invert_dst_factor, 0, 1);
}
static bool
is_dest_factor(enum blend_factor factor, bool alpha)
{
return factor == BLEND_FACTOR_DST_ALPHA ||
factor == BLEND_FACTOR_DST_COLOR ||
(factor == BLEND_FACTOR_SRC_ALPHA_SATURATE && !alpha);
}
/* Determines if a blend equation reads back the destination. This can occur by
* explicitly referencing the destination in the blend equation, or by using a
* partial writemask. */
bool
pan_blend_reads_dest(const struct pan_blend_equation equation)
{
return (equation.color_mask && equation.color_mask != 0xF) ||
is_dest_factor(equation.rgb_src_factor, false) ||
is_dest_factor(equation.alpha_src_factor, true) ||
equation.rgb_dst_factor != BLEND_FACTOR_ZERO ||
equation.rgb_invert_dst_factor ||
equation.alpha_dst_factor != BLEND_FACTOR_ZERO ||
equation.alpha_invert_dst_factor;
}
/* Create the descriptor for a fixed blend mode given the corresponding API
* state. Assumes the equation can be represented as fixed-function. */
void
pan_blend_to_fixed_function_equation(const struct pan_blend_equation equation,
struct MALI_BLEND_EQUATION *out)
{
/* If no blending is enabled, default back on `replace` mode */
if (!equation.blend_enable) {
out->color_mask = equation.color_mask;
out->rgb.a = MALI_BLEND_OPERAND_A_SRC;
out->rgb.b = MALI_BLEND_OPERAND_B_SRC;
out->rgb.c = MALI_BLEND_OPERAND_C_ZERO;
out->alpha.a = MALI_BLEND_OPERAND_A_SRC;
out->alpha.b = MALI_BLEND_OPERAND_B_SRC;
out->alpha.c = MALI_BLEND_OPERAND_C_ZERO;
return;
}
/* Compile the fixed-function blend */
to_panfrost_function(equation.rgb_func,
equation.rgb_src_factor,
equation.rgb_invert_src_factor,
equation.rgb_dst_factor,
equation.rgb_invert_dst_factor,
false, &out->rgb);
to_panfrost_function(equation.alpha_func,
equation.alpha_src_factor,
equation.alpha_invert_src_factor,
equation.alpha_dst_factor,
equation.alpha_invert_dst_factor,
true, &out->alpha);
out->color_mask = equation.color_mask;
}
uint32_t
pan_pack_blend(const struct pan_blend_equation equation)
{
STATIC_ASSERT(sizeof(uint32_t) == MALI_BLEND_EQUATION_LENGTH);
uint32_t out = 0;
pan_pack(&out, BLEND_EQUATION, cfg) {
pan_blend_to_fixed_function_equation(equation, &cfg);
}
return out;
}
static uint32_t pan_blend_shader_key_hash(const void *key)
{
return _mesa_hash_data(key, sizeof(struct pan_blend_shader_key));
}
static bool pan_blend_shader_key_equal(const void *a, const void *b)
{
return !memcmp(a, b, sizeof(struct pan_blend_shader_key));
}
void
pan_blend_shaders_init(struct panfrost_device *dev)
{
dev->blend_shaders.shaders =
_mesa_hash_table_create(NULL, pan_blend_shader_key_hash,
pan_blend_shader_key_equal);
pthread_mutex_init(&dev->blend_shaders.lock, NULL);
}
void
pan_blend_shaders_cleanup(struct panfrost_device *dev)
{
_mesa_hash_table_destroy(dev->blend_shaders.shaders, NULL);
}
#else /* ifndef PAN_ARCH */
static const char *
logicop_str(enum pipe_logicop logicop)
{
switch (logicop) {
case PIPE_LOGICOP_CLEAR: return "clear";
case PIPE_LOGICOP_NOR: return "nor";
case PIPE_LOGICOP_AND_INVERTED: return "and-inverted";
case PIPE_LOGICOP_COPY_INVERTED: return "copy-inverted";
case PIPE_LOGICOP_AND_REVERSE: return "and-reverse";
case PIPE_LOGICOP_INVERT: return "invert";
case PIPE_LOGICOP_XOR: return "xor";
case PIPE_LOGICOP_NAND: return "nand";
case PIPE_LOGICOP_AND: return "and";
case PIPE_LOGICOP_EQUIV: return "equiv";
case PIPE_LOGICOP_NOOP: return "noop";
case PIPE_LOGICOP_OR_INVERTED: return "or-inverted";
case PIPE_LOGICOP_COPY: return "copy";
case PIPE_LOGICOP_OR_REVERSE: return "or-reverse";
case PIPE_LOGICOP_OR: return "or";
case PIPE_LOGICOP_SET: return "set";
default: unreachable("Invalid logicop\n");
}
}
static void
get_equation_str(const struct pan_blend_rt_state *rt_state,
char *str, unsigned len)
{
const char *funcs[] = {
"add", "sub", "reverse_sub", "min", "max",
};
const char *factors[] = {
"zero", "src_color", "src1_color", "dst_color",
"src_alpha", "src1_alpha", "dst_alpha",
"const_color", "const_alpha", "src_alpha_sat",
};
int ret;
if (!rt_state->equation.blend_enable) {
ret = snprintf(str, len, "replace");
assert(ret > 0);
return;
}
if (rt_state->equation.color_mask & 7) {
assert(rt_state->equation.rgb_func < ARRAY_SIZE(funcs));
assert(rt_state->equation.rgb_src_factor < ARRAY_SIZE(factors));
assert(rt_state->equation.rgb_dst_factor < ARRAY_SIZE(factors));
ret = snprintf(str, len, "%s%s%s(func=%s,src_factor=%s%s,dst_factor=%s%s)%s",
(rt_state->equation.color_mask & 1) ? "R" : "",
(rt_state->equation.color_mask & 2) ? "G" : "",
(rt_state->equation.color_mask & 4) ? "B" : "",
funcs[rt_state->equation.rgb_func],
rt_state->equation.rgb_invert_src_factor ? "-" : "",
factors[rt_state->equation.rgb_src_factor],
rt_state->equation.rgb_invert_dst_factor ? "-" : "",
factors[rt_state->equation.rgb_dst_factor],
rt_state->equation.color_mask & 8 ? ";" : "");
assert(ret > 0);
str += ret;
len -= ret;
}
if (rt_state->equation.color_mask & 8) {
assert(rt_state->equation.alpha_func < ARRAY_SIZE(funcs));
assert(rt_state->equation.alpha_src_factor < ARRAY_SIZE(factors));
assert(rt_state->equation.alpha_dst_factor < ARRAY_SIZE(factors));
ret = snprintf(str, len, "A(func=%s,src_factor=%s%s,dst_factor=%s%s)",
funcs[rt_state->equation.alpha_func],
rt_state->equation.alpha_invert_src_factor ? "-" : "",
factors[rt_state->equation.alpha_src_factor],
rt_state->equation.alpha_invert_dst_factor ? "-" : "",
factors[rt_state->equation.alpha_dst_factor]);
assert(ret > 0);
str += ret;
len -= ret;
}
}
static bool
pan_inline_blend_constants(nir_builder *b, nir_instr *instr, void *data)
{
if (instr->type != nir_instr_type_intrinsic)
return false;
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
if (intr->intrinsic != nir_intrinsic_load_blend_const_color_rgba)
return false;
float *floats = data;
const nir_const_value constants[4] = {
{ .f32 = floats[0] },
{ .f32 = floats[1] },
{ .f32 = floats[2] },
{ .f32 = floats[3] }
};
b->cursor = nir_after_instr(instr);
nir_ssa_def *constant = nir_build_imm(b, 4, 32, constants);
nir_ssa_def_rewrite_uses(&intr->dest.ssa, constant);
nir_instr_remove(instr);
return true;
}
nir_shader *
GENX(pan_blend_create_shader)(const struct panfrost_device *dev,
const struct pan_blend_state *state,
nir_alu_type src0_type,
nir_alu_type src1_type,
unsigned rt)
{
const struct pan_blend_rt_state *rt_state = &state->rts[rt];
char equation_str[128] = { 0 };
get_equation_str(rt_state, equation_str, sizeof(equation_str));
nir_builder b =
nir_builder_init_simple_shader(MESA_SHADER_FRAGMENT,
GENX(pan_shader_get_compiler_options)(),
"pan_blend(rt=%d,fmt=%s,nr_samples=%d,%s=%s)",
rt, util_format_name(rt_state->format),
rt_state->nr_samples,
state->logicop_enable ? "logicop" : "equation",
state->logicop_enable ?
logicop_str(state->logicop_func) : equation_str);
const struct util_format_description *format_desc =
util_format_description(rt_state->format);
nir_alu_type nir_type = pan_unpacked_type_for_format(format_desc);
enum glsl_base_type glsl_type = nir_get_glsl_base_type_for_nir_type(nir_type);
nir_lower_blend_options options = {
.logicop_enable = state->logicop_enable,
.logicop_func = state->logicop_func,
.rt[0].colormask = rt_state->equation.color_mask,
.format[0] = rt_state->format
};
if (!rt_state->equation.blend_enable) {
static const nir_lower_blend_channel replace = {
.func = BLEND_FUNC_ADD,
.src_factor = BLEND_FACTOR_ZERO,
.invert_src_factor = true,
.dst_factor = BLEND_FACTOR_ZERO,
.invert_dst_factor = false,
};
options.rt[0].rgb = replace;
options.rt[0].alpha = replace;
} else {
options.rt[0].rgb.func = rt_state->equation.rgb_func;
options.rt[0].rgb.src_factor = rt_state->equation.rgb_src_factor;
options.rt[0].rgb.invert_src_factor = rt_state->equation.rgb_invert_src_factor;
options.rt[0].rgb.dst_factor = rt_state->equation.rgb_dst_factor;
options.rt[0].rgb.invert_dst_factor = rt_state->equation.rgb_invert_dst_factor;
options.rt[0].alpha.func = rt_state->equation.alpha_func;
options.rt[0].alpha.src_factor = rt_state->equation.alpha_src_factor;
options.rt[0].alpha.invert_src_factor = rt_state->equation.alpha_invert_src_factor;
options.rt[0].alpha.dst_factor = rt_state->equation.alpha_dst_factor;
options.rt[0].alpha.invert_dst_factor = rt_state->equation.alpha_invert_dst_factor;
}
nir_alu_type src_types[] = { src0_type ?: nir_type_float32, src1_type ?: nir_type_float32 };
/* HACK: workaround buggy TGSI shaders (u_blitter) */
for (unsigned i = 0; i < ARRAY_SIZE(src_types); ++i) {
src_types[i] = nir_alu_type_get_base_type(nir_type) |
nir_alu_type_get_type_size(src_types[i]);
}
nir_variable *c_src =
nir_variable_create(b.shader, nir_var_shader_in,
glsl_vector_type(nir_get_glsl_base_type_for_nir_type(src_types[0]), 4),
"gl_Color");
c_src->data.location = VARYING_SLOT_COL0;
nir_variable *c_src1 =
nir_variable_create(b.shader, nir_var_shader_in,
glsl_vector_type(nir_get_glsl_base_type_for_nir_type(src_types[1]), 4),
"gl_Color1");
c_src1->data.location = VARYING_SLOT_VAR0;
c_src1->data.driver_location = 1;
nir_variable *c_out =
nir_variable_create(b.shader, nir_var_shader_out,
glsl_vector_type(glsl_type, 4),
"gl_FragColor");
c_out->data.location = FRAG_RESULT_DATA0;
nir_ssa_def *s_src[] = {nir_load_var(&b, c_src), nir_load_var(&b, c_src1)};
/* Saturate integer conversions */
for (int i = 0; i < ARRAY_SIZE(s_src); ++i) {
nir_alu_type T = nir_alu_type_get_base_type(nir_type);
s_src[i] = nir_convert_with_rounding(&b, s_src[i],
src_types[i], nir_type,
nir_rounding_mode_undef,
T != nir_type_float);
}
/* Build a trivial blend shader */
nir_store_var(&b, c_out, s_src[0], 0xFF);
options.src1 = s_src[1];
NIR_PASS_V(b.shader, nir_lower_blend, &options);
nir_shader_instructions_pass(b.shader, pan_inline_blend_constants,
nir_metadata_block_index | nir_metadata_dominance,
(void *) state->constants);
return b.shader;
}
#if PAN_ARCH >= 6
uint64_t
GENX(pan_blend_get_internal_desc)(const struct panfrost_device *dev,
enum pipe_format fmt, unsigned rt,
unsigned force_size, bool dithered)
{
const struct util_format_description *desc = util_format_description(fmt);
uint64_t res;
pan_pack(&res, INTERNAL_BLEND, cfg) {
cfg.mode = MALI_BLEND_MODE_OPAQUE;
cfg.fixed_function.num_comps = desc->nr_channels;
cfg.fixed_function.rt = rt;
nir_alu_type T = pan_unpacked_type_for_format(desc);
if (force_size)
T = nir_alu_type_get_base_type(T) | force_size;
switch (T) {
case nir_type_float16:
cfg.fixed_function.conversion.register_format =
MALI_REGISTER_FILE_FORMAT_F16;
break;
case nir_type_float32:
cfg.fixed_function.conversion.register_format =
MALI_REGISTER_FILE_FORMAT_F32;
break;
case nir_type_int8:
case nir_type_int16:
cfg.fixed_function.conversion.register_format =
MALI_REGISTER_FILE_FORMAT_I16;
break;
case nir_type_int32:
cfg.fixed_function.conversion.register_format =
MALI_REGISTER_FILE_FORMAT_I32;
break;
case nir_type_uint8:
case nir_type_uint16:
cfg.fixed_function.conversion.register_format =
MALI_REGISTER_FILE_FORMAT_U16;
break;
case nir_type_uint32:
cfg.fixed_function.conversion.register_format =
MALI_REGISTER_FILE_FORMAT_U32;
break;
default:
unreachable("Invalid format");
}
cfg.fixed_function.conversion.memory_format =
panfrost_format_to_bifrost_blend(dev, fmt, dithered);
}
return res;
}
#endif
struct pan_blend_shader_variant *
GENX(pan_blend_get_shader_locked)(const struct panfrost_device *dev,
const struct pan_blend_state *state,
nir_alu_type src0_type,
nir_alu_type src1_type,
unsigned rt)
{
struct pan_blend_shader_key key = {
.format = state->rts[rt].format,
.src0_type = src0_type,
.src1_type = src1_type,
.rt = rt,
.has_constants = pan_blend_constant_mask(state->rts[rt].equation) != 0,
.logicop_enable = state->logicop_enable,
.logicop_func = state->logicop_func,
.nr_samples = state->rts[rt].nr_samples,
.equation = state->rts[rt].equation,
};
struct hash_entry *he = _mesa_hash_table_search(dev->blend_shaders.shaders, &key);
struct pan_blend_shader *shader = he ? he->data : NULL;
if (!shader) {
shader = rzalloc(dev->blend_shaders.shaders, struct pan_blend_shader);
shader->key = key;
list_inithead(&shader->variants);
_mesa_hash_table_insert(dev->blend_shaders.shaders, &shader->key, shader);
}
list_for_each_entry(struct pan_blend_shader_variant, iter,
&shader->variants, node) {
if (!key.has_constants ||
!memcmp(iter->constants, state->constants, sizeof(iter->constants))) {
return iter;
}
}
struct pan_blend_shader_variant *variant = NULL;
if (shader->nvariants < PAN_BLEND_SHADER_MAX_VARIANTS) {
variant = rzalloc(shader, struct pan_blend_shader_variant);
util_dynarray_init(&variant->binary, variant);
list_add(&variant->node, &shader->variants);
shader->nvariants++;
} else {
variant = list_last_entry(&shader->variants, struct pan_blend_shader_variant, node);
list_del(&variant->node);
list_add(&variant->node, &shader->variants);
util_dynarray_clear(&variant->binary);
}
memcpy(variant->constants, state->constants, sizeof(variant->constants));
nir_shader *nir =
GENX(pan_blend_create_shader)(dev, state, src0_type, src1_type, rt);
/* Compile the NIR shader */
struct panfrost_compile_inputs inputs = {
.gpu_id = dev->gpu_id,
.is_blend = true,
.blend.rt = shader->key.rt,
.blend.nr_samples = key.nr_samples,
.fixed_sysval_ubo = -1,
.rt_formats = { key.format },
};
#if PAN_ARCH >= 6
inputs.blend.bifrost_blend_desc =
GENX(pan_blend_get_internal_desc)(dev, key.format, key.rt, 0, false);
#endif
struct pan_shader_info info;
GENX(pan_shader_compile)(nir, &inputs, &variant->binary, &info);
/* Blend shaders can't have sysvals */
assert(info.sysvals.sysval_count == 0);
variant->work_reg_count = info.work_reg_count;
#if PAN_ARCH <= 5
variant->first_tag = info.midgard.first_tag;
#endif
ralloc_free(nir);
return variant;
}
#endif /* ifndef PAN_ARCH */