mesa/src/panfrost/lib/pan_blend.c
Antonio Ospite ddf2aa3a4d build: avoid redefining unreachable() which is standard in C23
In the C23 standard unreachable() is now a predefined function-like
macro in <stddef.h>

See https://android.googlesource.com/platform/bionic/+/HEAD/docs/c23.md#is-now-a-predefined-function_like-macro-in

And this causes build errors when building for C23:

-----------------------------------------------------------------------
In file included from ../src/util/log.h:30,
                 from ../src/util/log.c:30:
../src/util/macros.h:123:9: warning: "unreachable" redefined
  123 | #define unreachable(str)    \
      |         ^~~~~~~~~~~
In file included from ../src/util/macros.h:31:
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h:456:9: note: this is the location of the previous definition
  456 | #define unreachable() (__builtin_unreachable ())
      |         ^~~~~~~~~~~
-----------------------------------------------------------------------

So don't redefine it with the same name, but use the name UNREACHABLE()
to also signify it's a macro.

Using a different name also makes sense because the behavior of the
macro was extending the one of __builtin_unreachable() anyway, and it
also had a different signature, accepting one argument, compared to the
standard unreachable() with no arguments.

This change improves the chances of building mesa with the C23 standard,
which for instance is the default in recent AOSP versions.

All the instances of the macro, including the definition, were updated
with the following command line:

  git grep -l '[^_]unreachable(' -- "src/**" | sort | uniq | \
  while read file; \
  do \
    sed -e 's/\([^_]\)unreachable(/\1UNREACHABLE(/g' -i "$file"; \
  done && \
  sed -e 's/#undef unreachable/#undef UNREACHABLE/g' -i src/intel/isl/isl_aux_info.c

Reviewed-by: Erik Faye-Lund <erik.faye-lund@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36437>
2025-07-31 17:49:42 +00:00

800 lines
27 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"
#include "util/blend.h"
#ifdef PAN_ARCH
#include "pan_shader.h"
#include "pan_texture.h"
#endif
#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"
#include "util/format/u_format.h"
#ifndef PAN_ARCH
/* Fixed function blending */
static bool
factor_is_supported(enum pipe_blendfactor factor)
{
factor = util_blendfactor_without_invert(factor);
return factor != PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE &&
factor != PIPE_BLENDFACTOR_SRC1_COLOR &&
factor != PIPE_BLENDFACTOR_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 pipe_blend_func blend_func, enum pipe_blendfactor src_factor,
enum pipe_blendfactor dest_factor, bool is_alpha)
{
return (blend_func == PIPE_BLEND_ADD) &&
((src_factor == PIPE_BLENDFACTOR_DST_COLOR) ||
((src_factor == PIPE_BLENDFACTOR_DST_ALPHA) && is_alpha)) &&
((dest_factor == PIPE_BLENDFACTOR_SRC_COLOR) ||
((dest_factor == PIPE_BLENDFACTOR_SRC_ALPHA) && is_alpha));
}
static bool
can_fixed_function_equation(enum pipe_blend_func blend_func,
enum pipe_blendfactor src_factor,
enum pipe_blendfactor dest_factor, bool is_alpha,
bool supports_2src)
{
if (is_2srcdest(blend_func, src_factor, dest_factor, is_alpha))
return supports_2src;
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;
/* Fixed function requires src/dest factors to match (up to invert) or be
* zero/one.
*/
enum pipe_blendfactor src = util_blendfactor_without_invert(src_factor);
enum pipe_blendfactor dest = util_blendfactor_without_invert(dest_factor);
return (src == dest) || (src == PIPE_BLENDFACTOR_ONE) ||
(dest == PIPE_BLENDFACTOR_ONE);
}
static unsigned
blend_factor_constant_mask(enum pipe_blendfactor factor)
{
factor = util_blendfactor_without_invert(factor);
if (factor == PIPE_BLENDFACTOR_CONST_COLOR)
return 0b0111; /* RGB */
else if (factor == PIPE_BLENDFACTOR_CONST_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_dst_factor, false, supports_2src) &&
can_fixed_function_equation(
equation.alpha_func, equation.alpha_src_factor,
equation.alpha_dst_factor, true, supports_2src));
}
static enum mali_blend_operand_c
to_c_factor(enum pipe_blendfactor factor)
{
switch (util_blendfactor_without_invert(factor)) {
case PIPE_BLENDFACTOR_ONE:
/* Extra invert to flip back in caller */
return MALI_BLEND_OPERAND_C_ZERO;
case PIPE_BLENDFACTOR_SRC_ALPHA:
return MALI_BLEND_OPERAND_C_SRC_ALPHA;
case PIPE_BLENDFACTOR_DST_ALPHA:
return MALI_BLEND_OPERAND_C_DEST_ALPHA;
case PIPE_BLENDFACTOR_SRC_COLOR:
return MALI_BLEND_OPERAND_C_SRC;
case PIPE_BLENDFACTOR_DST_COLOR:
return MALI_BLEND_OPERAND_C_DEST;
case PIPE_BLENDFACTOR_CONST_COLOR:
case PIPE_BLENDFACTOR_CONST_ALPHA:
return MALI_BLEND_OPERAND_C_CONSTANT;
default:
UNREACHABLE("Unsupported blend factor");
}
}
static void
to_mali_function(enum pipe_blend_func blend_func,
enum pipe_blendfactor src_factor,
enum pipe_blendfactor dest_factor, bool is_alpha,
struct MALI_BLEND_FUNCTION *function)
{
assert(can_fixed_function_equation(blend_func, src_factor, dest_factor,
is_alpha, true));
/* We handle ZERO/ONE specially since it's the hardware has 0 and can invert
* to 1 but Gallium has 0 as the uninverted version.
*/
bool src_inverted =
util_blendfactor_is_inverted(src_factor) ^
(util_blendfactor_without_invert(src_factor) == PIPE_BLENDFACTOR_ONE);
bool dest_inverted =
util_blendfactor_is_inverted(dest_factor) ^
(util_blendfactor_without_invert(dest_factor) == PIPE_BLENDFACTOR_ONE);
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;
function->invert_c = dest_inverted;
function->c = to_c_factor(dest_factor);
} 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;
function->invert_c = dest_inverted;
function->c = to_c_factor(dest_factor);
} 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;
function->invert_c = src_inverted;
function->c = to_c_factor(src_factor);
} 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;
function->invert_c = src_inverted;
function->c = to_c_factor(src_factor);
} else if (src_factor == dest_factor) {
function->a = MALI_BLEND_OPERAND_A_ZERO;
function->invert_c = src_inverted;
function->c = to_c_factor(src_factor);
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;
FALLTHROUGH;
case PIPE_BLEND_SUBTRACT:
function->b = MALI_BLEND_OPERAND_B_SRC_MINUS_DEST;
break;
default:
UNREACHABLE("Invalid blend function");
}
} else if (is_2srcdest(blend_func, src_factor, dest_factor, 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(util_blendfactor_without_invert(src_factor) ==
util_blendfactor_without_invert(dest_factor) &&
src_inverted != dest_inverted);
function->a = MALI_BLEND_OPERAND_A_DEST;
function->invert_c = src_inverted;
function->c = to_c_factor(src_factor);
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;
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 == PIPE_BLENDFACTOR_ONE &&
equation.rgb_dst_factor == PIPE_BLENDFACTOR_ZERO &&
(equation.rgb_func == PIPE_BLEND_ADD ||
equation.rgb_func == PIPE_BLEND_SUBTRACT) &&
equation.alpha_src_factor == PIPE_BLENDFACTOR_ONE &&
equation.alpha_dst_factor == PIPE_BLENDFACTOR_ZERO &&
(equation.alpha_func == PIPE_BLEND_ADD ||
equation.alpha_func == PIPE_BLEND_SUBTRACT);
}
/* Check if a factor represents a constant value of val, assuming src_alpha is
* the given constant.
*/
static inline bool
is_factor_01(enum pipe_blendfactor factor, unsigned val, unsigned srca)
{
assert(val == 0 || val == 1);
assert(srca == 0 || srca == 1);
switch (factor) {
case PIPE_BLENDFACTOR_ZERO:
return (val == 0);
case PIPE_BLENDFACTOR_ONE:
return (val == 1);
case PIPE_BLENDFACTOR_SRC_ALPHA:
return (val == srca);
case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
return (val == (1 - srca));
default:
return false;
}
}
/* 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 != PIPE_BLEND_ADD &&
eq.rgb_func != PIPE_BLEND_REVERSE_SUBTRACT)
return false;
if (eq.color_mask & 0x8) {
if (!is_factor_01(eq.alpha_dst_factor, 1, 0))
return false;
}
if (eq.color_mask & 0x7) {
if (!is_factor_01(eq.rgb_dst_factor, 1, 0))
return false;
if (!is_factor_01(eq.rgb_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 != PIPE_BLEND_ADD && eq.rgb_func != PIPE_BLEND_SUBTRACT)
return false;
if (eq.color_mask != 0xf)
return false;
return is_factor_01(eq.rgb_src_factor, 1, 1) &&
is_factor_01(eq.alpha_src_factor, 1, 1) &&
is_factor_01(eq.rgb_dst_factor, 0, 1) &&
is_factor_01(eq.alpha_dst_factor, 0, 1);
}
static bool
is_dest_factor(enum pipe_blendfactor factor, bool alpha)
{
factor = util_blendfactor_without_invert(factor);
return factor == PIPE_BLENDFACTOR_DST_ALPHA ||
factor == PIPE_BLENDFACTOR_DST_COLOR ||
(factor == PIPE_BLENDFACTOR_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)
{
if (equation.color_mask && equation.color_mask != 0xF)
return true;
if (!equation.blend_enable)
return false;
return is_dest_factor(equation.rgb_src_factor, false) ||
is_dest_factor(equation.alpha_src_factor, true) ||
equation.rgb_dst_factor != PIPE_BLENDFACTOR_ZERO ||
equation.alpha_dst_factor != PIPE_BLENDFACTOR_ZERO;
}
/* 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_mali_function(equation.rgb_func, equation.rgb_src_factor,
equation.rgb_dst_factor, false, &out->rgb);
to_mali_function(equation.alpha_func, equation.alpha_src_factor,
equation.alpha_dst_factor, true, &out->alpha);
out->color_mask = equation.color_mask;
}
uint32_t
pan_pack_blend(const struct pan_blend_equation equation)
{
struct mali_blend_equation_packed out;
pan_pack(&out, BLEND_EQUATION, cfg) {
pan_blend_to_fixed_function_equation(equation, &cfg);
}
return out.opaque[0];
}
enum mali_register_file_format
pan_blend_type_from_nir(nir_alu_type nir_type)
{
switch (nir_type) {
case 0: /* Render target not in use */
return 0;
case nir_type_float16:
return MALI_REGISTER_FILE_FORMAT_F16;
case nir_type_float32:
return MALI_REGISTER_FILE_FORMAT_F32;
case nir_type_int32:
return MALI_REGISTER_FILE_FORMAT_I32;
case nir_type_uint32:
return MALI_REGISTER_FILE_FORMAT_U32;
case nir_type_int16:
return MALI_REGISTER_FILE_FORMAT_I16;
case nir_type_uint16:
return MALI_REGISTER_FILE_FORMAT_U16;
default:
UNREACHABLE("Unsupported blend shader type for NIR alu type");
return 0;
}
}
#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[] = {
"", "one", "src_color", "src_alpha", "dst_alpha",
"dst_color", "src_alpha_sat", "const_color", "const_alpha", "src1_color",
"src1_alpha",
};
int ret;
if (!rt_state->equation.blend_enable) {
ret = snprintf(str, len, "replace(%s%s%s%s)",
(rt_state->equation.color_mask & 1) ? "R" : "",
(rt_state->equation.color_mask & 2) ? "G" : "",
(rt_state->equation.color_mask & 4) ? "B" : "",
(rt_state->equation.color_mask & 8) ? "A" : "");
assert(ret > 0);
return;
}
if (rt_state->equation.color_mask & 7) {
assert(rt_state->equation.rgb_func < ARRAY_SIZE(funcs));
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],
util_blendfactor_is_inverted(rt_state->equation.rgb_src_factor) ? "-"
: "",
factors[util_blendfactor_without_invert(
rt_state->equation.rgb_src_factor)],
util_blendfactor_is_inverted(rt_state->equation.rgb_dst_factor) ? "-"
: "",
factors[util_blendfactor_without_invert(
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));
ret = snprintf(
str, len, "A(func=%s,src_factor=%s%s,dst_factor=%s%s)",
funcs[rt_state->equation.alpha_func],
util_blendfactor_is_inverted(rt_state->equation.alpha_src_factor) ? "-"
: "",
factors[util_blendfactor_without_invert(
rt_state->equation.alpha_src_factor)],
util_blendfactor_is_inverted(rt_state->equation.alpha_dst_factor) ? "-"
: "",
factors[util_blendfactor_without_invert(
rt_state->equation.alpha_dst_factor)]);
assert(ret > 0);
str += ret;
len -= ret;
}
}
nir_shader *
GENX(pan_blend_create_shader)(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, pan_shader_get_compiler_options(PAN_ARCH),
"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);
/* Bifrost/Valhall support 16-bit and 32-bit register formats for
* LD_TILE/ST_TILE/BLEND, but do not support 8-bit. Rather than making
* the fragment output 8-bit and inserting extra conversions in the
* compiler, promote the output to 16-bit. The larger size is still
* compatible with correct conversion semantics.
*/
if (PAN_ARCH >= 6 && nir_alu_type_get_type_size(nir_type) == 8)
nir_type = nir_alu_type_get_base_type(nir_type) | 16;
nir_lower_blend_options options = {
.logicop_enable = state->logicop_enable,
.logicop_func = state->logicop_func,
};
options.rt[rt].colormask = rt_state->equation.color_mask;
options.format[rt] = rt_state->format;
if (!rt_state->equation.blend_enable) {
static const nir_lower_blend_channel replace = {
.func = PIPE_BLEND_ADD,
.src_factor = PIPE_BLENDFACTOR_ONE,
.dst_factor = PIPE_BLENDFACTOR_ZERO,
};
options.rt[rt].rgb = replace;
options.rt[rt].alpha = replace;
} else {
options.rt[rt].rgb.func = rt_state->equation.rgb_func;
options.rt[rt].rgb.src_factor = rt_state->equation.rgb_src_factor;
options.rt[rt].rgb.dst_factor = rt_state->equation.rgb_dst_factor;
options.rt[rt].alpha.func = rt_state->equation.alpha_func;
options.rt[rt].alpha.src_factor = rt_state->equation.alpha_src_factor;
options.rt[rt].alpha.dst_factor = rt_state->equation.alpha_dst_factor;
}
nir_def *pixel = nir_load_barycentric_pixel(&b, 32, .interp_mode = 1);
nir_def *zero = nir_imm_int(&b, 0);
for (unsigned i = 0; i < 2; ++i) {
nir_alu_type src_type =
(i == 1 ? src1_type : src0_type) ?: nir_type_float32;
/* HACK: workaround buggy TGSI shaders (u_blitter) */
src_type = nir_alu_type_get_base_type(nir_type) |
nir_alu_type_get_type_size(src_type);
nir_def *src = nir_load_interpolated_input(
&b, 4, nir_alu_type_get_type_size(src_type), pixel, zero,
.io_semantics.location = i ? VARYING_SLOT_VAR0 : VARYING_SLOT_COL0,
.io_semantics.num_slots = 1, .base = i, .dest_type = src_type);
if (state->alpha_to_one && src_type == nir_type_float32) {
/* force alpha to 1 */
src = nir_vector_insert_imm(&b, src,
nir_imm_floatN_t(&b, 1.0, src->bit_size),
3);
}
/* On Midgard, the blend shader is responsible for format conversion.
* As the OpenGL spec requires integer conversions to saturate, we must
* saturate ourselves here. On Bifrost and later, the conversion
* hardware handles this automatically.
*/
nir_alu_type T = nir_alu_type_get_base_type(nir_type);
bool should_saturate = (PAN_ARCH <= 5) && (T != nir_type_float);
src = nir_convert_with_rounding(&b, src, T, nir_type,
nir_rounding_mode_undef, should_saturate);
nir_store_output(&b, src, zero, .write_mask = BITFIELD_MASK(4),
.src_type = nir_type,
.io_semantics.location = FRAG_RESULT_DATA0 + rt,
.io_semantics.num_slots = 1,
.io_semantics.dual_source_blend_index = i);
}
b.shader->info.io_lowered = true;
NIR_PASS(_, b.shader, nir_lower_blend, &options);
return b.shader;
}
#if PAN_ARCH >= 6
uint64_t
GENX(pan_blend_get_internal_desc)(enum pipe_format fmt, unsigned rt,
unsigned force_size, bool dithered)
{
const struct util_format_description *desc = util_format_description(fmt);
struct mali_internal_blend_packed 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 =
GENX(pan_dithered_format_from_pipe_format)(fmt, dithered);
}
return res.opaque[0] | ((uint64_t)res.opaque[1] << 32);
}
static bool
inline_rt_conversion(nir_builder *b, nir_intrinsic_instr *intr, void *data)
{
if (intr->intrinsic != nir_intrinsic_load_rt_conversion_pan)
return false;
enum pipe_format *formats = data;
unsigned rt = nir_intrinsic_base(intr);
unsigned size = nir_alu_type_get_type_size(nir_intrinsic_src_type(intr));
uint64_t conversion =
GENX(pan_blend_get_internal_desc)(formats[rt], rt, size, false);
b->cursor = nir_after_instr(&intr->instr);
nir_def_rewrite_uses(&intr->def, nir_imm_int(b, conversion >> 32));
return true;
}
bool
GENX(pan_inline_rt_conversion)(nir_shader *s, enum pipe_format *formats)
{
return nir_shader_intrinsics_pass(s, inline_rt_conversion,
nir_metadata_control_flow, formats);
}
#if PAN_ARCH < 9
enum mali_register_file_format
GENX(pan_fixup_blend_type)(nir_alu_type T_size, enum pipe_format format)
{
const struct util_format_description *desc = util_format_description(format);
unsigned size = nir_alu_type_get_type_size(T_size);
nir_alu_type T_format = pan_unpacked_type_for_format(desc);
nir_alu_type T = nir_alu_type_get_base_type(T_format) | size;
return pan_blend_type_from_nir(T);
}
#endif
#endif
#endif /* ifndef PAN_ARCH */