Merge branch 'hk-vk_blend_equation_advanced' into 'main'

asahi/hk: Implement VK_EXT_blend_operation_advanced

See merge request mesa/mesa!38929
This commit is contained in:
Christian Gmeiner 2025-12-20 02:11:21 +01:00
commit defb296403
21 changed files with 1156 additions and 267 deletions

View file

@ -597,7 +597,7 @@ Khronos extensions that are not part of any Vulkan version:
VK_EXT_acquire_xlib_display DONE (anv, hk, lvp, nvk, panvk, radv, tu, v3dv, vn)
VK_EXT_attachment_feedback_loop_dynamic_state DONE (anv, lvp, radv, tu, vn)
VK_EXT_attachment_feedback_loop_layout DONE (anv, hk, lvp, nvk, radv, tu, v3dv, vn)
VK_EXT_blend_operation_advanced DONE (vn)
VK_EXT_blend_operation_advanced DONE (hk, vn)
VK_EXT_border_color_swizzle DONE (anv, hasvk, hk, lvp, nvk, panvk, pvr, radv/gfx10+, tu, v3dv, vn)
VK_EXT_buffer_device_address DONE (anv, dzn/sm6.6+, hasvk, hk, nvk, panvk, radv, vn)
VK_EXT_calibrated_timestamps DONE (anv, hasvk, hk, kk, nvk, panvk/v10+, lvp, radv, vn, tu/a750+)

View file

@ -100,16 +100,92 @@ struct agx_fs_prolog_key {
};
struct agx_blend_rt_key {
unsigned colormask : 4;
unsigned advanced_blend : 1;
unsigned mode : 26;
};
static_assert(sizeof(struct agx_blend_rt_key) == 4, "packed");
struct agx_blend_standard {
enum pipe_blend_func rgb_func : 3;
enum pipe_blendfactor rgb_src_factor : 5;
enum pipe_blendfactor rgb_dst_factor : 5;
enum pipe_blend_func alpha_func : 3;
enum pipe_blendfactor alpha_src_factor : 5;
enum pipe_blendfactor alpha_dst_factor : 5;
unsigned colormask : 4;
unsigned pad : 2;
};
static_assert(sizeof(struct agx_blend_rt_key) == 4, "packed");
static inline unsigned
agx_pack_blend_standard(enum pipe_blend_func rgb_func,
enum pipe_blendfactor rgb_src_factor,
enum pipe_blendfactor rgb_dst_factor,
enum pipe_blend_func alpha_func,
enum pipe_blendfactor alpha_src_factor,
enum pipe_blendfactor alpha_dst_factor)
{
union {
struct agx_blend_standard s;
unsigned val;
} u = {.s = {
.rgb_func = rgb_func,
.rgb_src_factor = rgb_src_factor,
.rgb_dst_factor = rgb_dst_factor,
.alpha_func = alpha_func,
.alpha_src_factor = alpha_src_factor,
.alpha_dst_factor = alpha_dst_factor,
}};
return u.val;
}
static inline struct agx_blend_standard
agx_unpack_blend_standard(unsigned mode)
{
union {
struct agx_blend_standard a;
unsigned val;
} u = {.val = mode};
return u.a;
}
struct agx_blend_advanced {
enum pipe_advanced_blend_mode op : 8;
unsigned overlap : 2;
unsigned src_premultiplied : 1;
unsigned dst_premultiplied : 1;
unsigned clamp_results : 1;
};
static inline unsigned
agx_pack_blend_advanced(enum pipe_advanced_blend_mode op, unsigned overlap,
bool src_premultiplied, bool dst_premultiplied,
bool clamp_results)
{
union {
struct agx_blend_advanced a;
unsigned val;
} u = {.a = {
.op = op,
.overlap = overlap,
.src_premultiplied = src_premultiplied,
.dst_premultiplied = dst_premultiplied,
.clamp_results = clamp_results,
}};
return u.val;
}
static inline struct agx_blend_advanced
agx_unpack_blend_advanced(unsigned mode)
{
union {
struct agx_blend_advanced a;
unsigned val;
} u = {.val = mode};
return u.a;
}
struct agx_blend_key {
struct agx_blend_rt_key rt[8];

View file

@ -329,11 +329,14 @@ lower_tests_zs(nir_shader *s, bool value)
static inline bool
blend_uses_2src(struct agx_blend_rt_key rt)
{
assert(rt.advanced_blend == false);
const struct agx_blend_standard blend = agx_unpack_blend_standard(rt.mode);
enum pipe_blendfactor factors[] = {
rt.rgb_src_factor,
rt.rgb_dst_factor,
rt.alpha_src_factor,
rt.alpha_dst_factor,
blend.rgb_src_factor,
blend.rgb_dst_factor,
blend.alpha_src_factor,
blend.alpha_dst_factor,
};
for (unsigned i = 0; i < ARRAY_SIZE(factors); ++i) {
@ -394,7 +397,8 @@ agx_nir_fs_epilog(nir_builder *b, const void *key_)
* for blending so should be suppressed for missing attachments to keep
* the assert from blowing up on OpenGL.
*/
if (blend_uses_2src(key->blend.rt[rt]) &&
if (!key->blend.rt[rt].advanced_blend &&
blend_uses_2src(key->blend.rt[rt]) &&
key->rt_formats[rt] != PIPE_FORMAT_NONE) {
assert(location == 0);
@ -433,17 +437,34 @@ agx_nir_fs_epilog(nir_builder *b, const void *key_)
for (unsigned i = 0; i < 8; ++i) {
opts.format[i] = key->rt_formats[i];
opts.rt[i] = (nir_lower_blend_rt){
.rgb.src_factor = key->blend.rt[i].rgb_src_factor,
.rgb.dst_factor = key->blend.rt[i].rgb_dst_factor,
.rgb.func = key->blend.rt[i].rgb_func,
if (key->blend.rt[i].advanced_blend) {
const struct agx_blend_advanced blend =
agx_unpack_blend_advanced(key->blend.rt[i].mode);
.alpha.src_factor = key->blend.rt[i].alpha_src_factor,
.alpha.dst_factor = key->blend.rt[i].alpha_dst_factor,
.alpha.func = key->blend.rt[i].alpha_func,
opts.rt[i] = (nir_lower_blend_rt){
.advanced_blend = true,
.colormask = key->blend.rt[i].colormask,
.blend_mode = blend.op,
.src_premultiplied = blend.src_premultiplied,
.dst_premultiplied = blend.dst_premultiplied,
.overlap = blend.overlap,
};
} else {
const struct agx_blend_standard blend =
agx_unpack_blend_standard(key->blend.rt[i].mode);
.colormask = key->blend.rt[i].colormask,
};
opts.rt[i] = (nir_lower_blend_rt){
.rgb.src_factor = blend.rgb_src_factor,
.rgb.dst_factor = blend.rgb_dst_factor,
.rgb.func = blend.rgb_func,
.alpha.src_factor = blend.alpha_src_factor,
.alpha.dst_factor = blend.alpha_dst_factor,
.alpha.func = blend.alpha_func,
.colormask = key->blend.rt[i].colormask,
};
}
}
/* It's more efficient to use masked stores (with

View file

@ -128,6 +128,7 @@ hk_cmd_buffer_dirty_render_pass(struct hk_cmd_buffer *cmd)
BITSET_SET(dyn->dirty, MESA_VK_DYNAMIC_CB_BLEND_ENABLES);
BITSET_SET(dyn->dirty, MESA_VK_DYNAMIC_CB_BLEND_EQUATIONS);
BITSET_SET(dyn->dirty, MESA_VK_DYNAMIC_CB_WRITE_MASKS);
BITSET_SET(dyn->dirty, MESA_VK_DYNAMIC_CB_BLEND_ADVANCED);
/* These depend on the depth/stencil format */
BITSET_SET(dyn->dirty, MESA_VK_DYNAMIC_DS_DEPTH_TEST_ENABLE);
@ -2796,7 +2797,7 @@ hk_flush_dynamic_state(struct hk_cmd_buffer *cmd, struct hk_cs *cs,
IS_DIRTY(CB_LOGIC_OP_ENABLE) || IS_DIRTY(CB_WRITE_MASKS) ||
IS_DIRTY(CB_COLOR_WRITE_ENABLES) || IS_DIRTY(CB_ATTACHMENT_COUNT) ||
IS_DIRTY(CB_BLEND_ENABLES) || IS_DIRTY(CB_BLEND_EQUATIONS) ||
IS_DIRTY(CB_BLEND_CONSTANTS) ||
IS_DIRTY(CB_BLEND_CONSTANTS) || IS_DIRTY(CB_BLEND_ADVANCED) ||
desc->root_dirty /* for pipeline stats */ || true) {
unsigned tib_sample_mask = BITFIELD_MASK(dyn->ms.rasterization_samples);
@ -2890,32 +2891,31 @@ hk_flush_dynamic_state(struct hk_cmd_buffer *cmd, struct hk_cs *cs,
if (!dyn->cb.attachments[i].blend_enable) {
key.epilog.blend.rt[i] = (struct agx_blend_rt_key){
.colormask = write_mask,
.rgb_func = PIPE_BLEND_ADD,
.alpha_func = PIPE_BLEND_ADD,
.rgb_src_factor = PIPE_BLENDFACTOR_ONE,
.alpha_src_factor = PIPE_BLENDFACTOR_ONE,
.rgb_dst_factor = PIPE_BLENDFACTOR_ZERO,
.alpha_dst_factor = PIPE_BLENDFACTOR_ZERO,
.mode = agx_pack_blend_standard(
PIPE_BLEND_ADD, PIPE_BLENDFACTOR_ONE, PIPE_BLENDFACTOR_ZERO,
PIPE_BLEND_ADD, PIPE_BLENDFACTOR_ONE, PIPE_BLENDFACTOR_ZERO),
};
} else if (cb->color_blend_op >= VK_BLEND_OP_ZERO_EXT) {
key.epilog.blend.rt[i] = (struct agx_blend_rt_key){
.colormask = write_mask,
.advanced_blend = 1,
.mode = agx_pack_blend_advanced(
vk_advanced_blend_op_to_pipe(cb->color_blend_op),
(unsigned int)cb->blend_overlap, cb->src_premultiplied,
cb->dst_premultiplied, cb->clamp_results),
};
assert(cb->clamp_results == false);
} else {
key.epilog.blend.rt[i] = (struct agx_blend_rt_key){
.colormask = write_mask,
.rgb_src_factor =
.mode = agx_pack_blend_standard(
vk_blend_op_to_pipe(cb->color_blend_op),
vk_blend_factor_to_pipe(cb->src_color_blend_factor),
.rgb_dst_factor =
vk_blend_factor_to_pipe(cb->dst_color_blend_factor),
.rgb_func = vk_blend_op_to_pipe(cb->color_blend_op),
.alpha_src_factor =
vk_blend_op_to_pipe(cb->alpha_blend_op),
vk_blend_factor_to_pipe(cb->src_alpha_blend_factor),
.alpha_dst_factor =
vk_blend_factor_to_pipe(cb->dst_alpha_blend_factor),
.alpha_func = vk_blend_op_to_pipe(cb->alpha_blend_op),
vk_blend_factor_to_pipe(cb->dst_alpha_blend_factor)),
};
}
}

View file

@ -138,6 +138,7 @@ hk_get_device_extensions(const struct hk_instance *instance,
.KHR_zero_initialize_workgroup_memory = true,
.EXT_4444_formats = true,
.EXT_attachment_feedback_loop_layout = true,
.EXT_blend_operation_advanced = true,
.EXT_border_color_swizzle = true,
.EXT_buffer_device_address = true,
.EXT_calibrated_timestamps = true,
@ -623,6 +624,9 @@ hk_get_device_features(
/* VK_KHR_shader_relaxed_extended_instruction */
.shaderRelaxedExtendedInstruction = true,
/* VK_EXT_blend_operation_advanced */
.advancedBlendCoherentOperations = true,
};
}
@ -996,6 +1000,14 @@ hk_get_device_properties(const struct agx_device *dev,
/* VK_KHR_fragment_shader_barycentric */
.triStripVertexOrderIndependentOfProvokingVertex = false,
/* VK_EXT_blend_operation_advanced */
.advancedBlendMaxColorAttachments = HK_MAX_RTS,
.advancedBlendIndependentBlend = true,
.advancedBlendNonPremultipliedSrcColor = true,
.advancedBlendNonPremultipliedDstColor = true,
.advancedBlendCorrelatedOverlap = true,
.advancedBlendAllOperations = true,
};
strncpy(properties->deviceName, dev->name, sizeof(properties->deviceName));

View file

@ -25,6 +25,7 @@
#include "nir.h"
#include "nir_builder.h"
#include "nir_blend_equation_advanced_helper.h"
#include "gl_nir.h"
#include "program/prog_instruction.h"
@ -70,123 +71,6 @@ swizzle_w(nir_builder *b, nir_def *src)
return nir_channel(b, src, 3);
}
static nir_def *
blend_multiply(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = Cs*Cd */
return nir_fmul(b, src, dst);
}
static nir_def *
blend_screen(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = Cs+Cd-Cs*Cd */
return nir_fsub(b, nir_fadd(b, src, dst), nir_fmul(b, src, dst));
}
static nir_def *
blend_overlay(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = 2*Cs*Cd, if Cd <= 0.5
* 1-2*(1-Cs)*(1-Cd), otherwise
*/
nir_def *rule_1 = nir_fmul(b, nir_fmul(b, src, dst), imm3(b, 2.0));
nir_def *rule_2 =
nir_fsub(b, imm3(b, 1.0), nir_fmul(b, nir_fmul(b, nir_fsub(b, imm3(b, 1.0), src), nir_fsub(b, imm3(b, 1.0), dst)), imm3(b, 2.0)));
return nir_bcsel(b, nir_fge(b, imm3(b, 0.5f), dst), rule_1, rule_2);
}
static nir_def *
blend_darken(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = min(Cs,Cd) */
return nir_fmin(b, src, dst);
}
static nir_def *
blend_lighten(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = max(Cs,Cd) */
return nir_fmax(b, src, dst);
}
static nir_def *
blend_colordodge(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) =
* 0, if Cd <= 0
* min(1,Cd/(1-Cs)), if Cd > 0 and Cs < 1
* 1, if Cd > 0 and Cs >= 1
*/
return nir_bcsel(b, nir_fge(b, imm3(b, 0.0), dst), imm3(b, 0.0),
nir_bcsel(b, nir_fge(b, src, imm3(b, 1.0)), imm3(b, 1.0),
nir_fmin(b, imm3(b, 1.0), nir_fdiv(b, dst, nir_fsub(b, imm3(b, 1.0), src)))));
}
static nir_def *
blend_colorburn(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) =
* 1, if Cd >= 1
* 1 - min(1,(1-Cd)/Cs), if Cd < 1 and Cs > 0
* 0, if Cd < 1 and Cs <= 0
*/
return nir_bcsel(b, nir_fge(b, dst, imm3(b, 1.0)), imm3(b, 1.0),
nir_bcsel(b, nir_fge(b, imm3(b, 0.0), src), imm3(b, 0.0),
nir_fsub(b, imm3(b, 1.0), nir_fmin(b, imm3(b, 1.0), nir_fdiv(b, nir_fsub(b, imm3(b, 1.0), dst), src)))));
}
static nir_def *
blend_hardlight(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = 2*Cs*Cd, if Cs <= 0.5
* 1-2*(1-Cs)*(1-Cd), otherwise
*/
nir_def *rule_1 = nir_fmul(b, imm3(b, 2.0), nir_fmul(b, src, dst));
nir_def *rule_2 =
nir_fsub(b, imm3(b, 1.0), nir_fmul(b, imm3(b, 2.0), nir_fmul(b, nir_fsub(b, imm3(b, 1.0), src), nir_fsub(b, imm3(b, 1.0), dst))));
return nir_bcsel(b, nir_fge(b, imm3(b, 0.5), src), rule_1, rule_2);
}
static nir_def *
blend_softlight(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) =
* Cd-(1-2*Cs)*Cd*(1-Cd),
* if Cs <= 0.5
* Cd+(2*Cs-1)*Cd*((16*Cd-12)*Cd+3),
* if Cs > 0.5 and Cd <= 0.25
* Cd+(2*Cs-1)*(sqrt(Cd)-Cd),
* if Cs > 0.5 and Cd > 0.25
*
* We can simplify this to
*
* f(Cs,Cd) = Cd+(2*Cs-1)*g(Cs,Cd) where
* g(Cs,Cd) = Cd*Cd-Cd if Cs <= 0.5
* Cd*((16*Cd-12)*Cd+3) if Cs > 0.5 and Cd <= 0.25
* sqrt(Cd)-Cd, otherwise
*/
nir_def *factor_1 = nir_fmul(b, dst, nir_fsub(b, imm3(b, 1.0), dst));
nir_def *factor_2 =
nir_fmul(b, dst, nir_fadd(b, nir_fmul(b, nir_fsub(b, nir_fmul(b, imm3(b, 16.0), dst), imm3(b, 12.0)), dst), imm3(b, 3.0)));
nir_def *factor_3 = nir_fsub(b, nir_fsqrt(b, dst), dst);
nir_def *factor = nir_bcsel(b, nir_fge(b, imm3(b, 0.5), src), factor_1,
nir_bcsel(b, nir_fge(b, imm3(b, 0.25), dst), factor_2, factor_3));
return nir_fadd(b, dst, nir_fmul(b, nir_fsub(b, nir_fmul(b, imm3(b, 2.0), src), imm3(b, 1.0)), factor));
}
static nir_def *
blend_difference(nir_builder *b, nir_def *src, nir_def *dst)
{
return nir_fabs(b, nir_fsub(b, dst, src));
}
static nir_def *
blend_exclusion(nir_builder *b, nir_def *src, nir_def *dst)
{
return nir_fadd(b, src, nir_fsub(b, dst, nir_fmul(b, imm3(b, 2.0), nir_fmul(b, src, dst))));
}
/* Return the minimum of a vec3's components */
static nir_def *
minv3(nir_builder *b, nir_def *v)
@ -303,7 +187,7 @@ set_lum_sat(nir_builder *b,
}
static nir_def *
is_mode(nir_builder *b, nir_variable *mode, enum gl_advanced_blend_mode q)
is_mode(nir_builder *b, nir_variable *mode, enum pipe_advanced_blend_mode q)
{
return nir_ieq_imm(b, nir_load_var(b, mode), (unsigned) q);
}
@ -318,7 +202,7 @@ calc_blend_result(nir_builder *b,
nir_variable *result = add_temp_var(b, "__blend_result", glsl_vec4_type());
/* If we're not doing advanced blending, just write the original value. */
nir_if *if_blending = nir_push_if(b, is_mode(b, mode, BLEND_NONE));
nir_if *if_blending = nir_push_if(b, is_mode(b, mode, PIPE_ADVANCED_BLEND_NONE));
nir_store_var(b, result, blend_src, ~0);
nir_push_else(b, if_blending);
@ -362,59 +246,61 @@ calc_blend_result(nir_builder *b,
unsigned choices = blend_qualifiers;
while (choices) {
enum gl_advanced_blend_mode choice = (enum gl_advanced_blend_mode)u_bit_scan(&choices);
enum pipe_advanced_blend_mode choice = (enum pipe_advanced_blend_mode)u_bit_scan(&choices);
nir_if *iff = nir_push_if(b, is_mode(b, mode, choice));
nir_def *val = NULL;
switch (choice) {
case BLEND_MULTIPLY:
case PIPE_ADVANCED_BLEND_MULTIPLY:
val = blend_multiply(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_SCREEN:
case PIPE_ADVANCED_BLEND_SCREEN:
val = blend_screen(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_OVERLAY:
case PIPE_ADVANCED_BLEND_OVERLAY:
val = blend_overlay(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_DARKEN:
case PIPE_ADVANCED_BLEND_DARKEN:
val = blend_darken(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_LIGHTEN:
case PIPE_ADVANCED_BLEND_LIGHTEN:
val = blend_lighten(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_COLORDODGE:
case PIPE_ADVANCED_BLEND_COLORDODGE:
val = blend_colordodge(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_COLORBURN:
case PIPE_ADVANCED_BLEND_COLORBURN:
val = blend_colorburn(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_HARDLIGHT:
case PIPE_ADVANCED_BLEND_HARDLIGHT:
val = blend_hardlight(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_SOFTLIGHT:
case PIPE_ADVANCED_BLEND_SOFTLIGHT:
val = blend_softlight(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_DIFFERENCE:
case PIPE_ADVANCED_BLEND_DIFFERENCE:
val = blend_difference(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_EXCLUSION:
case PIPE_ADVANCED_BLEND_EXCLUSION:
val = blend_exclusion(b, src_rgb_def, dst_rgb_def);
break;
case BLEND_HSL_HUE:
case PIPE_ADVANCED_BLEND_HSL_HUE:
set_lum_sat(b, factor, src_rgb, dst_rgb, dst_rgb);
break;
case BLEND_HSL_SATURATION:
case PIPE_ADVANCED_BLEND_HSL_SATURATION:
set_lum_sat(b, factor, dst_rgb, src_rgb, dst_rgb);
break;
case BLEND_HSL_COLOR:
case PIPE_ADVANCED_BLEND_HSL_COLOR:
set_lum(b, factor, src_rgb, dst_rgb);
break;
case BLEND_HSL_LUMINOSITY:
case PIPE_ADVANCED_BLEND_HSL_LUMINOSITY:
set_lum(b, factor, dst_rgb, src_rgb);
break;
case BLEND_NONE:
case PIPE_ADVANCED_BLEND_NONE:
UNREACHABLE("not real cases");
default:
UNREACHABLE("not supported advanced blend mode");
}
if (val)

View file

@ -32,6 +32,7 @@
#include "ast.h"
#include "glsl_parser_extras.h"
#include "compiler/glsl_types.h"
#include "util/blend.h"
#include "util/u_string.h"
#include "util/format/u_format.h"
#include "main/consts_exts.h"
@ -1612,22 +1613,22 @@ layout_qualifier_id:
const char *s;
uint32_t mask;
} map[] = {
{ "blend_support_multiply", BITFIELD_BIT(BLEND_MULTIPLY) },
{ "blend_support_screen", BITFIELD_BIT(BLEND_SCREEN) },
{ "blend_support_overlay", BITFIELD_BIT(BLEND_OVERLAY) },
{ "blend_support_darken", BITFIELD_BIT(BLEND_DARKEN) },
{ "blend_support_lighten", BITFIELD_BIT(BLEND_LIGHTEN) },
{ "blend_support_colordodge", BITFIELD_BIT(BLEND_COLORDODGE) },
{ "blend_support_colorburn", BITFIELD_BIT(BLEND_COLORBURN) },
{ "blend_support_hardlight", BITFIELD_BIT(BLEND_HARDLIGHT) },
{ "blend_support_softlight", BITFIELD_BIT(BLEND_SOFTLIGHT) },
{ "blend_support_difference", BITFIELD_BIT(BLEND_DIFFERENCE) },
{ "blend_support_exclusion", BITFIELD_BIT(BLEND_EXCLUSION) },
{ "blend_support_hsl_hue", BITFIELD_BIT(BLEND_HSL_HUE) },
{ "blend_support_hsl_saturation", BITFIELD_BIT(BLEND_HSL_SATURATION) },
{ "blend_support_hsl_color", BITFIELD_BIT(BLEND_HSL_COLOR) },
{ "blend_support_hsl_luminosity", BITFIELD_BIT(BLEND_HSL_LUMINOSITY) },
{ "blend_support_all_equations", (1u << (BLEND_HSL_LUMINOSITY + 1)) - 2 },
{ "blend_support_multiply", BITFIELD_BIT(PIPE_ADVANCED_BLEND_MULTIPLY) },
{ "blend_support_screen", BITFIELD_BIT(PIPE_ADVANCED_BLEND_SCREEN) },
{ "blend_support_overlay", BITFIELD_BIT(PIPE_ADVANCED_BLEND_OVERLAY) },
{ "blend_support_darken", BITFIELD_BIT(PIPE_ADVANCED_BLEND_DARKEN) },
{ "blend_support_lighten", BITFIELD_BIT(PIPE_ADVANCED_BLEND_LIGHTEN) },
{ "blend_support_colordodge", BITFIELD_BIT(PIPE_ADVANCED_BLEND_COLORDODGE) },
{ "blend_support_colorburn", BITFIELD_BIT(PIPE_ADVANCED_BLEND_COLORBURN) },
{ "blend_support_hardlight", BITFIELD_BIT(PIPE_ADVANCED_BLEND_HARDLIGHT) },
{ "blend_support_softlight", BITFIELD_BIT(PIPE_ADVANCED_BLEND_SOFTLIGHT) },
{ "blend_support_difference", BITFIELD_BIT(PIPE_ADVANCED_BLEND_DIFFERENCE) },
{ "blend_support_exclusion", BITFIELD_BIT(PIPE_ADVANCED_BLEND_EXCLUSION) },
{ "blend_support_hsl_hue", BITFIELD_BIT(PIPE_ADVANCED_BLEND_HSL_HUE) },
{ "blend_support_hsl_saturation", BITFIELD_BIT(PIPE_ADVANCED_BLEND_HSL_SATURATION) },
{ "blend_support_hsl_color", BITFIELD_BIT(PIPE_ADVANCED_BLEND_HSL_COLOR) },
{ "blend_support_hsl_luminosity", BITFIELD_BIT(PIPE_ADVANCED_BLEND_HSL_LUMINOSITY) },
{ "blend_support_all_equations", (1u << (PIPE_ADVANCED_BLEND_HSL_LUMINOSITY + 1)) - 2 },
};
for (unsigned i = 0; i < ARRAY_SIZE(map); i++) {
if (match_layout_qualifier($1, map[i].s, state) == 0) {

View file

@ -0,0 +1,132 @@
/*
* Copyright © 2023 Timothy Arceri <tarceri@itsqueeze.com>
* Copyright © 2016 Intel Corporation
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "nir.h"
#include "nir_builder.h"
static inline nir_def *
imm3(nir_builder *b, float x)
{
return nir_imm_vec3(b, x, x, x);
}
static inline nir_def *
blend_multiply(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = Cs*Cd */
return nir_fmul(b, src, dst);
}
static inline nir_def *
blend_screen(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = Cs+Cd-Cs*Cd */
return nir_fsub(b, nir_fadd(b, src, dst), nir_fmul(b, src, dst));
}
static inline nir_def *
blend_overlay(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = 2*Cs*Cd, if Cd <= 0.5
* 1-2*(1-Cs)*(1-Cd), otherwise
*/
nir_def *rule_1 = nir_fmul(b, nir_fmul(b, src, dst), imm3(b, 2.0));
nir_def *rule_2 =
nir_fsub(b, imm3(b, 1.0), nir_fmul(b, nir_fmul(b, nir_fsub(b, imm3(b, 1.0), src), nir_fsub(b, imm3(b, 1.0), dst)), imm3(b, 2.0)));
return nir_bcsel(b, nir_fge(b, imm3(b, 0.5f), dst), rule_1, rule_2);
}
static inline nir_def *
blend_darken(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = min(Cs,Cd) */
return nir_fmin(b, src, dst);
}
static inline nir_def *
blend_lighten(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = max(Cs,Cd) */
return nir_fmax(b, src, dst);
}
static inline nir_def *
blend_colordodge(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) =
* 0, if Cd <= 0
* min(1,Cd/(1-Cs)), if Cd > 0 and Cs < 1
* 1, if Cd > 0 and Cs >= 1
*/
return nir_bcsel(b, nir_fge(b, imm3(b, 0.0), dst), imm3(b, 0.0),
nir_bcsel(b, nir_fge(b, src, imm3(b, 1.0)), imm3(b, 1.0),
nir_fmin(b, imm3(b, 1.0), nir_fdiv(b, dst, nir_fsub(b, imm3(b, 1.0), src)))));
}
static inline nir_def *
blend_colorburn(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) =
* 1, if Cd >= 1
* 1 - min(1,(1-Cd)/Cs), if Cd < 1 and Cs > 0
* 0, if Cd < 1 and Cs <= 0
*/
return nir_bcsel(b, nir_fge(b, dst, imm3(b, 1.0)), imm3(b, 1.0),
nir_bcsel(b, nir_fge(b, imm3(b, 0.0), src), imm3(b, 0.0),
nir_fsub(b, imm3(b, 1.0), nir_fmin(b, imm3(b, 1.0), nir_fdiv(b, nir_fsub(b, imm3(b, 1.0), dst), src)))));
}
static inline nir_def *
blend_hardlight(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) = 2*Cs*Cd, if Cs <= 0.5
* 1-2*(1-Cs)*(1-Cd), otherwise
*/
nir_def *rule_1 = nir_fmul(b, imm3(b, 2.0), nir_fmul(b, src, dst));
nir_def *rule_2 =
nir_fsub(b, imm3(b, 1.0), nir_fmul(b, imm3(b, 2.0), nir_fmul(b, nir_fsub(b, imm3(b, 1.0), src), nir_fsub(b, imm3(b, 1.0), dst))));
return nir_bcsel(b, nir_fge(b, imm3(b, 0.5), src), rule_1, rule_2);
}
static inline nir_def *
blend_softlight(nir_builder *b, nir_def *src, nir_def *dst)
{
/* f(Cs,Cd) =
* Cd-(1-2*Cs)*Cd*(1-Cd),
* if Cs <= 0.5
* Cd+(2*Cs-1)*Cd*((16*Cd-12)*Cd+3),
* if Cs > 0.5 and Cd <= 0.25
* Cd+(2*Cs-1)*(sqrt(Cd)-Cd),
* if Cs > 0.5 and Cd > 0.25
*
* We can simplify this to
*
* f(Cs,Cd) = Cd+(2*Cs-1)*g(Cs,Cd) where
* g(Cs,Cd) = Cd*Cd-Cd if Cs <= 0.5
* Cd*((16*Cd-12)*Cd+3) if Cs > 0.5 and Cd <= 0.25
* sqrt(Cd)-Cd, otherwise
*/
nir_def *factor_1 = nir_fmul(b, dst, nir_fsub(b, imm3(b, 1.0), dst));
nir_def *factor_2 =
nir_fmul(b, dst, nir_fadd(b, nir_fmul(b, nir_fsub(b, nir_fmul(b, imm3(b, 16.0), dst), imm3(b, 12.0)), dst), imm3(b, 3.0)));
nir_def *factor_3 = nir_fsub(b, nir_fsqrt(b, dst), dst);
nir_def *factor = nir_bcsel(b, nir_fge(b, imm3(b, 0.5), src), factor_1,
nir_bcsel(b, nir_fge(b, imm3(b, 0.25), dst), factor_2, factor_3));
return nir_fadd(b, dst, nir_fmul(b, nir_fsub(b, nir_fmul(b, imm3(b, 2.0), src), imm3(b, 1.0)), factor));
}
static inline nir_def *
blend_difference(nir_builder *b, nir_def *src, nir_def *dst)
{
return nir_fabs(b, nir_fsub(b, dst, src));
}
static inline nir_def *
blend_exclusion(nir_builder *b, nir_def *src, nir_def *dst)
{
return nir_fadd(b, src, nir_fsub(b, dst, nir_fmul(b, imm3(b, 2.0), nir_fmul(b, src, dst))));
}

View file

@ -1,4 +1,5 @@
/*
* Copyright (C) 2025 Google LLC
* Copyright (C) 2019-2021 Collabora, Ltd.
* Copyright (C) 2019 Alyssa Rosenzweig
*
@ -33,9 +34,11 @@
#include "nir_lower_blend.h"
#include "compiler/nir/nir.h"
#include "compiler/nir/nir_blend_equation_advanced_helper.h"
#include "compiler/nir/nir_builder.h"
#include "compiler/nir/nir_format_convert.h"
#include "util/blend.h"
#include "nir_builder_opcodes.h"
struct ctx {
const nir_lower_blend_options *options;
@ -326,6 +329,546 @@ channel_exists(const struct util_format_description *desc, unsigned i)
desc->channel[i].type != UTIL_FORMAT_TYPE_VOID;
}
static nir_def *
minv3(nir_builder *b, nir_def *v)
{
return nir_fmin(b, nir_fmin(b, nir_channel(b, v, 0), nir_channel(b, v, 1)),
nir_channel(b, v, 2));
}
static nir_def *
maxv3(nir_builder *b, nir_def *v)
{
return nir_fmax(b, nir_fmax(b, nir_channel(b, v, 0), nir_channel(b, v, 1)),
nir_channel(b, v, 2));
}
static nir_def *
lumv3(nir_builder *b, nir_def *c)
{
return nir_fdot(b, c, nir_imm_vec3(b, 0.30, 0.59, 0.11));
}
static nir_def *
satv3(nir_builder *b, nir_def *c)
{
return nir_fsub(b, maxv3(b, c), minv3(b, c));
}
/* Clip color to [0,1] while preserving luminosity */
static nir_def *
clip_color(nir_builder *b, nir_def *c)
{
nir_def *lum = lumv3(b, c);
nir_def *mincol = minv3(b, c);
nir_def *maxcol = maxv3(b, c);
/* If min < 0: c = lum + (c - lum) * lum / (lum - min) */
nir_def *t1 = nir_fdiv(b,
nir_fmul(b, nir_fsub(b, c, lum), lum),
nir_fsub(b, lum, mincol));
nir_def *c1 = nir_fadd(b, lum, t1);
/* If max > 1: c = lum + (c - lum) * (1 - lum) / (max - lum) */
nir_def *t2 = nir_fdiv(b,
nir_fmul(b, nir_fsub(b, c, lum), nir_fsub_imm(b, 1.0, lum)),
nir_fsub(b, maxcol, lum));
nir_def *c2 = nir_fadd(b, lum, t2);
nir_def *min_neg = nir_flt_imm(b, mincol, 0.0);
nir_def *max_gt1 = nir_fgt_imm(b, maxcol, 1.0);
return nir_bcsel(b, min_neg, c1,
nir_bcsel(b, max_gt1, c2, c));
}
/* Set luminosity of cbase to match clum */
static nir_def *
set_lum(nir_builder *b, nir_def *cbase, nir_def *clum)
{
nir_def *lbase = lumv3(b, cbase);
nir_def *llum = lumv3(b, clum);
nir_def *diff = nir_fsub(b, llum, lbase);
nir_def *c = nir_fadd(b, cbase, diff);
return clip_color(b, c);
}
/* Set saturation of cbase to match csat, then luminosity to match clum */
static nir_def *
set_lum_sat(nir_builder *b, nir_def *cbase, nir_def *csat, nir_def *clum)
{
nir_def *sbase = satv3(b, cbase);
nir_def *ssat = satv3(b, csat);
nir_def *minbase = minv3(b, cbase);
/* Scale saturation: (cbase - min) * ssat / sbase */
nir_def *scaled = nir_bcsel(b,
nir_fgt_imm(b, sbase, 0.0),
nir_fdiv(b, nir_fmul(b, nir_fsub(b, cbase, minbase), ssat), sbase),
imm3(b, 0.0));
return set_lum(b, scaled, clum);
}
static nir_def *
blend_hsl_hue(nir_builder *b, nir_def *src, nir_def *dst)
{
/* Hue from src, saturation and luminosity from dst */
return set_lum_sat(b, src, dst, dst);
}
static nir_def *
blend_hsl_saturation(nir_builder *b, nir_def *src, nir_def *dst)
{
/* Saturation from src, hue and luminosity from dst */
return set_lum_sat(b, dst, src, dst);
}
static nir_def *
blend_hsl_color(nir_builder *b, nir_def *src, nir_def *dst)
{
/* Hue and saturation from src, luminosity from dst */
return set_lum(b, src, dst);
}
static nir_def *
blend_hsl_luminosity(nir_builder *b, nir_def *src, nir_def *dst)
{
/* Luminosity from src, hue and saturation from dst */
return set_lum(b, dst, src);
}
static nir_def *
blend_invert(nir_builder *b, nir_def *src, nir_def *dst)
{
return nir_fsub_imm(b, 1.0, dst);
}
static nir_def *
blend_invert_rgb(nir_builder *b, nir_def *src, nir_def *dst)
{
return nir_fmul(b, src, nir_fsub_imm(b, 1.0, dst));
}
static nir_def *
blend_lineardodge(nir_builder *b, nir_def *src, nir_def *dst)
{
/* min(1, src + dst) */
return nir_fmin(b, imm3(b, 1.0), nir_fadd(b, src, dst));
}
static nir_def *
blend_linearburn(nir_builder *b, nir_def *src, nir_def *dst)
{
/* max(0, src + dst - 1) */
return nir_fmax(b, nir_imm_float(b, 0.0),
nir_fadd(b, src, nir_fadd_imm(b, dst, -1.0)));
}
static nir_def *
blend_vividlight(nir_builder *b, nir_def *src, nir_def *dst)
{
/*
* if src <= 0: 0
* if src < 0.5: 1 - min(1, (1-dst) / (2*src))
* if src < 1: min(1, dst / (2*(1-src)))
* else: 1
*/
nir_def *two_src = nir_fmul_imm(b, src, 2.0);
nir_def *one_minus_dst = nir_fsub_imm(b, 1.0, dst);
nir_def *one_minus_src = nir_fsub_imm(b, 1.0, src);
nir_def *case_lt_half = nir_fsub_imm(b, 1.0,
nir_fmin(b, imm3(b, 1.0), nir_fdiv(b, one_minus_dst, two_src)));
nir_def *case_lt_one = nir_fmin(b, imm3(b, 1.0),
nir_fdiv(b, dst, nir_fmul_imm(b, one_minus_src, 2.0)));
return nir_bcsel(b, nir_fle_imm(b, src, 0.0), imm3(b, 0.0),
nir_bcsel(b, nir_flt_imm(b, src, 0.5), case_lt_half,
nir_bcsel(b, nir_flt_imm(b, src, 1.0), case_lt_one,
imm3(b, 1.0))));
}
static nir_def *
blend_linearlight(nir_builder *b, nir_def *src, nir_def *dst)
{
/*
* if 2*src + dst > 2: 1
* if 2*src + dst <= 1: 0
* else: 2*src + dst - 1
*/
nir_def *two_src = nir_fmul_imm(b, src, 2.0);
nir_def *sum = nir_fadd(b, two_src, dst);
nir_def *result = nir_fsub(b, sum, imm3(b, 1.0));
return nir_bcsel(b, nir_fgt_imm(b, sum, 2.0), imm3(b, 1.0),
nir_bcsel(b, nir_fge(b, imm3(b, 1.0), sum), imm3(b, 0.0),
result));
}
static nir_def *
blend_pinlight(nir_builder *b, nir_def *src, nir_def *dst)
{
/*
* if (2*src - 1 > dst) && src < 0.5: 0
* if (2*src - 1 > dst) && src >= 0.5: 2*src - 1
* if (2*src - 1 <= dst) && src < 0.5*dst: 2*src
* if (2*src - 1 <= dst) && src >= 0.5*dst: dst
*/
nir_def *two_src = nir_fmul_imm(b, src, 2.0);
nir_def *two_src_minus_1 = nir_fsub(b, two_src, imm3(b, 1.0));
nir_def *half_dst = nir_fmul_imm(b, dst, 0.5);
nir_def *cond1 = nir_flt(b, dst, two_src_minus_1);
nir_def *cond2 = nir_flt_imm(b, src, 0.5);
nir_def *cond3 = nir_flt(b, src, half_dst);
return nir_bcsel(b, cond1,
nir_bcsel(b, cond2, imm3(b, 0.0), two_src_minus_1),
nir_bcsel(b, cond3, two_src, dst));
}
static nir_def *
blend_hardmix(nir_builder *b, nir_def *src, nir_def *dst)
{
/* if src + dst >= 1: 1, else 0.
* Use small epsilon to handle 8-bit quantization.
*/
nir_def *sum = nir_fadd(b, src, dst);
nir_def *threshold = nir_imm_float(b, 1.0 - 0.5 / 255.0); /* ~0.998039 */
return nir_bcsel(b, nir_fge(b, sum, threshold),
imm3(b, 1.0), imm3(b, 0.0));
}
/*
* Calculate the blend factor f(Cs', Cd').
* Returns NULL for blend modes where X=0, meaning f() is not used.
*/
static nir_def *
calc_blend_factor(nir_builder *b, enum pipe_advanced_blend_mode blend_op, nir_def *src, nir_def *dst)
{
switch (blend_op) {
/* f() result unused (X=0) */
case PIPE_ADVANCED_BLEND_NONE:
case PIPE_ADVANCED_BLEND_SRC_OUT:
case PIPE_ADVANCED_BLEND_DST_OUT:
case PIPE_ADVANCED_BLEND_XOR:
return NULL;
/* Standard blend modes */
case PIPE_ADVANCED_BLEND_MULTIPLY:
return blend_multiply(b, src, dst);
case PIPE_ADVANCED_BLEND_SCREEN:
return blend_screen(b, src, dst);
case PIPE_ADVANCED_BLEND_OVERLAY:
return blend_overlay(b, src, dst);
case PIPE_ADVANCED_BLEND_DARKEN:
return blend_darken(b, src, dst);
case PIPE_ADVANCED_BLEND_LIGHTEN:
return blend_lighten(b, src, dst);
case PIPE_ADVANCED_BLEND_COLORDODGE:
return blend_colordodge(b, src, dst);
case PIPE_ADVANCED_BLEND_COLORBURN:
return blend_colorburn(b, src, dst);
case PIPE_ADVANCED_BLEND_HARDLIGHT:
return blend_hardlight(b, src, dst);
case PIPE_ADVANCED_BLEND_SOFTLIGHT:
return blend_softlight(b, src, dst);
case PIPE_ADVANCED_BLEND_DIFFERENCE:
return blend_difference(b, src, dst);
case PIPE_ADVANCED_BLEND_EXCLUSION:
return blend_exclusion(b, src, dst);
/* HSL blend modes */
case PIPE_ADVANCED_BLEND_HSL_HUE:
return blend_hsl_hue(b, src, dst);
case PIPE_ADVANCED_BLEND_HSL_SATURATION:
return blend_hsl_saturation(b, src, dst);
case PIPE_ADVANCED_BLEND_HSL_COLOR:
return blend_hsl_color(b, src, dst);
case PIPE_ADVANCED_BLEND_HSL_LUMINOSITY:
return blend_hsl_luminosity(b, src, dst);
/* Porter-Duff modes where f(Cs,Cd) = Cs or Cd */
case PIPE_ADVANCED_BLEND_SRC:
case PIPE_ADVANCED_BLEND_SRC_OVER:
case PIPE_ADVANCED_BLEND_SRC_IN:
case PIPE_ADVANCED_BLEND_SRC_ATOP:
return src;
case PIPE_ADVANCED_BLEND_DST:
case PIPE_ADVANCED_BLEND_DST_OVER:
case PIPE_ADVANCED_BLEND_DST_IN:
case PIPE_ADVANCED_BLEND_DST_ATOP:
return dst;
/* Extended blend modes */
case PIPE_ADVANCED_BLEND_INVERT:
return blend_invert(b, src, dst);
case PIPE_ADVANCED_BLEND_INVERT_RGB:
return blend_invert_rgb(b, src, dst);
case PIPE_ADVANCED_BLEND_LINEARDODGE:
return blend_lineardodge(b, src, dst);
case PIPE_ADVANCED_BLEND_LINEARBURN:
return blend_linearburn(b, src, dst);
case PIPE_ADVANCED_BLEND_VIVIDLIGHT:
return blend_vividlight(b, src, dst);
case PIPE_ADVANCED_BLEND_LINEARLIGHT:
return blend_linearlight(b, src, dst);
case PIPE_ADVANCED_BLEND_PINLIGHT:
return blend_pinlight(b, src, dst);
case PIPE_ADVANCED_BLEND_HARDMIX:
return blend_hardmix(b, src, dst);
default:
UNREACHABLE("Invalid advanced blend op");
}
}
static nir_def *
calc_additional_rgb_blend(nir_builder *b, const nir_lower_blend_options *options,
unsigned rt,
nir_def *src, nir_def *dst)
{
nir_def *src_rgb = nir_trim_vector(b, src, 3);
nir_def *dst_rgb = nir_trim_vector(b, dst, 3);
nir_def *src_a = nir_channel(b, src, 3);
nir_def *dst_a = nir_channel(b, dst, 3);
/* Premultiply if non-premultiplied */
if (!options->rt[rt].src_premultiplied)
src_rgb = nir_fmul(b, src_rgb, src_a);
if (!options->rt[rt].dst_premultiplied)
dst_rgb = nir_fmul(b, dst_rgb, dst_a);
nir_def *rgb, *a;
switch (options->rt[rt].blend_mode) {
case PIPE_ADVANCED_BLEND_PLUS:
rgb = nir_fadd(b, src_rgb, dst_rgb);
a = nir_fadd(b, src_a, dst_a);
break;
case PIPE_ADVANCED_BLEND_PLUS_CLAMPED:
rgb = nir_fmin(b, imm3(b, 1.0), nir_fadd(b, src_rgb, dst_rgb));
a = nir_fmin(b, nir_imm_float(b, 1.0), nir_fadd(b, src_a, dst_a));
break;
case PIPE_ADVANCED_BLEND_PLUS_CLAMPED_ALPHA: {
nir_def *max_a = nir_fmin(b, nir_imm_float(b, 1.0), nir_fadd(b, src_a, dst_a));
rgb = nir_fmin(b, max_a, nir_fadd(b, src_rgb, dst_rgb));
a = max_a;
break;
}
case PIPE_ADVANCED_BLEND_PLUS_DARKER: {
nir_def *max_a = nir_fmin(b, nir_imm_float(b, 1.0), nir_fadd(b, src_a, dst_a));
/* max(0, max_a - ((src_a - src_rgb) + (dst_a - dst_rgb))) */
nir_def *src_diff = nir_fsub(b, src_a, src_rgb);
nir_def *dst_diff = nir_fsub(b, dst_a, dst_rgb);
rgb = nir_fmax(b, imm3(b, 0.0), nir_fsub(b, max_a, nir_fadd(b, src_diff, dst_diff)));
a = max_a;
break;
}
case PIPE_ADVANCED_BLEND_MINUS:
rgb = nir_fsub(b, dst_rgb, src_rgb);
a = nir_fsub(b, dst_a, src_a);
break;
case PIPE_ADVANCED_BLEND_MINUS_CLAMPED:
rgb = nir_fmax(b, imm3(b, 0.0), nir_fsub(b, dst_rgb, src_rgb));
a = nir_fmax(b, nir_imm_float(b, 0.0), nir_fsub(b, dst_a, src_a));
break;
case PIPE_ADVANCED_BLEND_CONTRAST: {
/* res.rgb = (dst_a / 2) + 2 * (dst_rgb - dst_a/2) * (src_rgb - src_a/2) */
nir_def *half_dst_a = nir_fmul_imm(b, dst_a, 0.5);
nir_def *half_src_a = nir_fmul_imm(b, src_a, 0.5);
nir_def *dst_centered = nir_fsub(b, dst_rgb, half_dst_a);
nir_def *src_centered = nir_fsub(b, src_rgb, half_src_a);
rgb = nir_fadd(b, half_dst_a,
nir_fmul_imm(b, nir_fmul(b, dst_centered, src_centered), 2.0));
a = dst_a;
break;
}
case PIPE_ADVANCED_BLEND_INVERT_OVG: {
/* res.rgb = src_a * (1 - dst_rgb) + (1 - src_a) * dst_rgb */
nir_def *one_minus_dst = nir_fsub_imm(b, 1.0, dst_rgb);
nir_def *one_minus_src_a = nir_fsub_imm(b, 1.0, src_a);
rgb = nir_fadd(b, nir_fmul(b, src_a, one_minus_dst),
nir_fmul(b, one_minus_src_a, dst_rgb));
a = nir_fsub(b, nir_fadd(b, src_a, dst_a), nir_fmul(b, src_a, dst_a));
break;
}
case PIPE_ADVANCED_BLEND_RED:
rgb = nir_vec3(b, nir_channel(b, src_rgb, 0), nir_channel(b, dst_rgb, 1), nir_channel(b, dst_rgb, 2));
a = dst_a;
break;
case PIPE_ADVANCED_BLEND_GREEN:
rgb = nir_vec3(b, nir_channel(b, dst_rgb, 0), nir_channel(b, src_rgb, 1), nir_channel(b, dst_rgb, 2));
a = dst_a;
break;
case PIPE_ADVANCED_BLEND_BLUE:
rgb = nir_vec3(b, nir_channel(b, dst_rgb, 0), nir_channel(b, dst_rgb, 1), nir_channel(b, src_rgb, 2));
a = dst_a;
break;
default:
UNREACHABLE("Invalid additional RGB blend op");
}
/* If dst is non-premultiplied, the output should also be non-premultiplied */
if (!options->rt[rt].dst_premultiplied) {
rgb = nir_bcsel(b,
nir_fgt_imm(b, a, 0.0),
nir_fdiv(b, rgb, a),
imm3(b, 0.0));
}
return nir_vec4(b, nir_channel(b, rgb, 0), nir_channel(b, rgb, 1),
nir_channel(b, rgb, 2), a);
}
/*
* X, Y, Z blend factors for the advanced blend equation:
* RGB = f(Cs',Cd') * X * p0 + Cs' * Y * p1 + Cd' * Z * p2
* A = X * p0 + Y * p1 + Z * p2
*
* Index by enum pipe_advanced_blend_mode.
* Modes >= PIPE_ADVANCED_BLEND_PLUS use separate calc_additional_rgb_blend().
*/
static const float blend_xyz[][3] = {
[PIPE_ADVANCED_BLEND_NONE] = { 0, 0, 0 },
[PIPE_ADVANCED_BLEND_MULTIPLY] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_SCREEN] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_OVERLAY] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_DARKEN] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_LIGHTEN] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_COLORDODGE] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_COLORBURN] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_HARDLIGHT] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_SOFTLIGHT] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_DIFFERENCE] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_EXCLUSION] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_HSL_HUE] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_HSL_SATURATION] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_HSL_COLOR] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_HSL_LUMINOSITY] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_SRC] = { 1, 1, 0 },
[PIPE_ADVANCED_BLEND_DST] = { 1, 0, 1 },
[PIPE_ADVANCED_BLEND_SRC_OVER] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_DST_OVER] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_SRC_IN] = { 1, 0, 0 },
[PIPE_ADVANCED_BLEND_DST_IN] = { 1, 0, 0 },
[PIPE_ADVANCED_BLEND_SRC_OUT] = { 0, 1, 0 },
[PIPE_ADVANCED_BLEND_DST_OUT] = { 0, 0, 1 },
[PIPE_ADVANCED_BLEND_SRC_ATOP] = { 1, 0, 1 },
[PIPE_ADVANCED_BLEND_DST_ATOP] = { 1, 1, 0 },
[PIPE_ADVANCED_BLEND_XOR] = { 0, 1, 1 },
[PIPE_ADVANCED_BLEND_INVERT] = { 1, 0, 1 },
[PIPE_ADVANCED_BLEND_INVERT_RGB] = { 1, 0, 1 },
[PIPE_ADVANCED_BLEND_LINEARDODGE] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_LINEARBURN] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_VIVIDLIGHT] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_LINEARLIGHT] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_PINLIGHT] = { 1, 1, 1 },
[PIPE_ADVANCED_BLEND_HARDMIX] = { 1, 1, 1 },
};
static nir_def *
nir_blend_advanced(
nir_builder *b,
const nir_lower_blend_options *options,
unsigned rt,
nir_def *src, nir_def *dst)
{
/* Check if this is an additional RGB blend op */
if (options->rt[rt].blend_mode >= PIPE_ADVANCED_BLEND_PLUS &&
options->rt[rt].blend_mode <= PIPE_ADVANCED_BLEND_BLUE) {
return calc_additional_rgb_blend(b, options, rt, src, dst);
}
nir_def *src_rgb = nir_trim_vector(b, src, 3);
nir_def *dst_rgb = nir_trim_vector(b, dst, 3);
nir_def *src_a = nir_channel(b, src, 3);
nir_def *dst_a = nir_channel(b, dst, 3);
/* Unpremultiply */
nir_def *src_rgb_unpre;
if (options->rt[rt].src_premultiplied) {
src_rgb_unpre = nir_bcsel(b,
nir_feq_imm(b, src_a, 0.0),
imm3(b, 0.0),
nir_fdiv(b, src_rgb, src_a));
} else {
src_rgb_unpre = src_rgb;
}
nir_def *dst_rgb_unpre;
if (options->rt[rt].dst_premultiplied) {
dst_rgb_unpre = nir_bcsel(b,
nir_feq_imm(b, dst_a, 0.0),
imm3(b, 0.0),
nir_fdiv(b, dst_rgb, dst_a));
} else {
dst_rgb_unpre = dst_rgb;
}
/* f(Cs', Cd') - may be NULL if X=0 (result unused) */
nir_def *factor = calc_blend_factor(b, options->rt[rt].blend_mode, src_rgb_unpre, dst_rgb_unpre);
nir_def *p0, *p1, *p2;
switch (options->rt[rt].overlap) {
case 0:
/* p0 = As * Ad, p1 = As * (1 - Ad), p2 = Ad * (1 - As) */
p0 = nir_fmul(b, src_a, dst_a);
p1 = nir_fmul(b, src_a, nir_fsub_imm(b, 1.0, dst_a));
p2 = nir_fmul(b, dst_a, nir_fsub_imm(b, 1.0, src_a));
break;
case 1:
/* p0 = max(As + Ad - 1, 0), p1 = min(As, 1 - Ad), p2 = min(Ad, 1 - As) */
p0 = nir_fmax(b, nir_fadd_imm(b, nir_fadd(b, src_a, dst_a), -1.0), nir_imm_float(b, 0.0));
p1 = nir_fmin(b, src_a, nir_fsub_imm(b, 1.0, dst_a));
p2 = nir_fmin(b, dst_a, nir_fsub_imm(b, 1.0, src_a));
break;
case 2:
/* p0 = min(As, Ad), p1 = max(As - Ad, 0), p2 = max(Ad - As, 0) */
p0 = nir_fmin(b, src_a, dst_a);
p1 = nir_fmax(b, nir_fsub(b, src_a, dst_a), nir_imm_float(b, 0.0));
p2 = nir_fmax(b, nir_fsub(b, dst_a, src_a), nir_imm_float(b, 0.0));
break;
default:
UNREACHABLE("unknown overlap");
}
const float x = blend_xyz[options->rt[rt].blend_mode][0];
const float y = blend_xyz[options->rt[rt].blend_mode][1];
const float z = blend_xyz[options->rt[rt].blend_mode][2];
/* RGB = f * X * p0 + Cs' * Y * p1 + Cd' * Z * p2 */
nir_def *rgb = imm3(b, 0.0);
if (factor)
rgb = nir_fmul(b, factor, nir_fmul_imm(b, p0, x));
if (y != 0.0)
rgb = nir_fadd(b, rgb, nir_fmul(b, src_rgb_unpre, nir_fmul_imm(b, p1, y)));
if (z != 0.0)
rgb = nir_fadd(b, rgb, nir_fmul(b, dst_rgb_unpre, nir_fmul_imm(b, p2, z)));
/* A = X * p0 + Y * p1 + Z * p2 */
nir_def *a = nir_imm_float(b, 0.0);
if (x != 0.0)
a = nir_fmul_imm(b, p0, x);
if (y != 0.0)
a = nir_fadd(b, a, nir_fmul_imm(b, p1, y));
if (z != 0.0)
a = nir_fadd(b, a, nir_fmul_imm(b, p2, z));
/* If dst is non-premultiplied, the output should also be non-premultiplied */
if (!options->rt[rt].dst_premultiplied) {
rgb = nir_bcsel(b,
nir_fgt_imm(b, a, 0.0),
nir_fdiv(b, rgb, a),
imm3(b, 0.0));
}
return nir_vec4(b, nir_channel(b, rgb, 0), nir_channel(b, rgb, 1),
nir_channel(b, rgb, 2), a);
}
/* Given a blend state, the source color, and the destination color,
* return the blended color
*/
@ -512,7 +1055,8 @@ nir_lower_blend_instr(nir_builder *b, nir_intrinsic_instr *store, void *data)
/* Grab the previous fragment color if we need it */
nir_def *dst;
if (channel_uses_dest(options->rt[rt].rgb) ||
if (options->rt[rt].advanced_blend ||
channel_uses_dest(options->rt[rt].rgb) ||
channel_uses_dest(options->rt[rt].alpha) ||
options->logicop_enable ||
options->rt[rt].colormask != BITFIELD_MASK(4)) {
@ -530,16 +1074,22 @@ nir_lower_blend_instr(nir_builder *b, nir_intrinsic_instr *store, void *data)
dst = nir_undef(b, 4, nir_src_bit_size(store->src[0]));
}
/* Blend the two colors per the passed options. We only call nir_blend if
* blending is enabled with a blend mode other than replace (independent of
* the color mask). That avoids unnecessary fsat instructions in the common
* case where blending is disabled at an API level, but the driver calls
/* Blend the two colors per the passed options. Blending is prioritized as:
* 1. Logic operations (if logicop_enable is true) - mutually exclusive with blending
* 2. Advanced blending (if advanced_blend is set) - uses complex blend equations
* 3. Standard blending (if configured) - uses traditional blend factors
*
* We only call nir_blend if blending is enabled with a blend mode other than replace
* (independent of the color mask). That avoids unnecessary fsat instructions in the
* common case where blending is disabled at an API level, but the driver calls
* nir_blend (possibly for color masking).
*/
nir_def *blended = src;
if (options->logicop_enable) {
blended = nir_blend_logicop(b, format, logicop_func, src, dst);
} else if (options->rt[rt].advanced_blend) {
blended = nir_blend_advanced(b, options, rt, src, dst);
} else if (!util_format_is_pure_integer(format) &&
!nir_blend_replace_rt(&options->rt[rt])) {
assert(!util_format_is_scaled(format));

View file

@ -44,7 +44,13 @@ typedef struct {
nir_lower_blend_channel alpha;
/* 4-bit colormask. 0x0 for none, 0xF for RGBA, 0x1 for R */
unsigned colormask;
unsigned colormask:4;
unsigned advanced_blend:1;
enum pipe_advanced_blend_mode blend_mode;
bool src_premultiplied;
bool dst_premultiplied;
unsigned overlap; /* 0=uncorrelated, 1=conjoint, 2=disjoint */
} nir_lower_blend_rt;
typedef struct {

View file

@ -1255,29 +1255,6 @@ enum gl_access_qualifier
ACCESS_FUSED_EU_DISABLE_INTEL = (1 << 19),
};
/**
* \brief Blend support qualifiers
*/
enum gl_advanced_blend_mode
{
BLEND_NONE = 0,
BLEND_MULTIPLY,
BLEND_SCREEN,
BLEND_OVERLAY,
BLEND_DARKEN,
BLEND_LIGHTEN,
BLEND_COLORDODGE,
BLEND_COLORBURN,
BLEND_HARDLIGHT,
BLEND_SOFTLIGHT,
BLEND_DIFFERENCE,
BLEND_EXCLUSION,
BLEND_HSL_HUE,
BLEND_HSL_SATURATION,
BLEND_HSL_COLOR,
BLEND_HSL_LUMINOSITY,
};
enum gl_tess_spacing
{
TESS_SPACING_UNSPECIFIED,

View file

@ -455,61 +455,62 @@ legal_simple_blend_equation(const struct gl_context *ctx, GLenum mode)
}
}
static enum gl_advanced_blend_mode
static enum pipe_advanced_blend_mode
advanced_blend_mode_from_gl_enum(GLenum mode)
{
switch (mode) {
case GL_MULTIPLY_KHR:
return BLEND_MULTIPLY;
return PIPE_ADVANCED_BLEND_MULTIPLY;
case GL_SCREEN_KHR:
return BLEND_SCREEN;
return PIPE_ADVANCED_BLEND_SCREEN;
case GL_OVERLAY_KHR:
return BLEND_OVERLAY;
return PIPE_ADVANCED_BLEND_OVERLAY;
case GL_DARKEN_KHR:
return BLEND_DARKEN;
return PIPE_ADVANCED_BLEND_DARKEN;
case GL_LIGHTEN_KHR:
return BLEND_LIGHTEN;
return PIPE_ADVANCED_BLEND_LIGHTEN;
case GL_COLORDODGE_KHR:
return BLEND_COLORDODGE;
return PIPE_ADVANCED_BLEND_COLORDODGE;
case GL_COLORBURN_KHR:
return BLEND_COLORBURN;
return PIPE_ADVANCED_BLEND_COLORBURN;
case GL_HARDLIGHT_KHR:
return BLEND_HARDLIGHT;
return PIPE_ADVANCED_BLEND_HARDLIGHT;
case GL_SOFTLIGHT_KHR:
return BLEND_SOFTLIGHT;
return PIPE_ADVANCED_BLEND_SOFTLIGHT;
case GL_DIFFERENCE_KHR:
return BLEND_DIFFERENCE;
return PIPE_ADVANCED_BLEND_DIFFERENCE;
case GL_EXCLUSION_KHR:
return BLEND_EXCLUSION;
return PIPE_ADVANCED_BLEND_EXCLUSION;
case GL_HSL_HUE_KHR:
return BLEND_HSL_HUE;
return PIPE_ADVANCED_BLEND_HSL_HUE;
case GL_HSL_SATURATION_KHR:
return BLEND_HSL_SATURATION;
return PIPE_ADVANCED_BLEND_HSL_SATURATION;
case GL_HSL_COLOR_KHR:
return BLEND_HSL_COLOR;
return PIPE_ADVANCED_BLEND_HSL_COLOR;
case GL_HSL_LUMINOSITY_KHR:
return BLEND_HSL_LUMINOSITY;
return PIPE_ADVANCED_BLEND_HSL_LUMINOSITY;
default:
return BLEND_NONE;
return PIPE_ADVANCED_BLEND_NONE;
}
}
/**
* If \p mode is one of the advanced blending equations defined by
* GL_KHR_blend_equation_advanced (and the extension is supported),
* return the corresponding BLEND_* enum. Otherwise, return BLEND_NONE
* (which can also be treated as false).
* return the corresponding PIPE_ADVANCED_BLEND_* enum.
* Otherwise, return PIPE_ADVANCED_BLEND_NONE (which can also be
* treated as false).
*/
static enum gl_advanced_blend_mode
static enum pipe_advanced_blend_mode
advanced_blend_mode(const struct gl_context *ctx, GLenum mode)
{
return _mesa_has_KHR_blend_equation_advanced(ctx) ?
advanced_blend_mode_from_gl_enum(mode) : BLEND_NONE;
advanced_blend_mode_from_gl_enum(mode) : PIPE_ADVANCED_BLEND_NONE;
}
static void
set_advanced_blend_mode(struct gl_context *ctx,
enum gl_advanced_blend_mode advanced_mode)
enum pipe_advanced_blend_mode advanced_mode)
{
if (ctx->Color._AdvancedBlendMode != advanced_mode) {
ctx->Color._AdvancedBlendMode = advanced_mode;
@ -525,7 +526,7 @@ _mesa_BlendEquation( GLenum mode )
const unsigned numBuffers = num_buffers(ctx);
unsigned buf;
bool changed = false;
enum gl_advanced_blend_mode advanced_mode = advanced_blend_mode(ctx, mode);
enum pipe_advanced_blend_mode advanced_mode = advanced_blend_mode(ctx, mode);
if (MESA_VERBOSE & VERBOSE_API)
_mesa_debug(ctx, "glBlendEquation(%s)\n",
@ -575,7 +576,7 @@ _mesa_BlendEquation( GLenum mode )
*/
static void
blend_equationi(struct gl_context *ctx, GLuint buf, GLenum mode,
enum gl_advanced_blend_mode advanced_mode)
enum pipe_advanced_blend_mode advanced_mode)
{
if (ctx->Color.Blend[buf].EquationRGB == mode &&
ctx->Color.Blend[buf].EquationA == mode)
@ -597,7 +598,7 @@ _mesa_BlendEquationiARB_no_error(GLuint buf, GLenum mode)
{
GET_CURRENT_CONTEXT(ctx);
enum gl_advanced_blend_mode advanced_mode = advanced_blend_mode(ctx, mode);
enum pipe_advanced_blend_mode advanced_mode = advanced_blend_mode(ctx, mode);
blend_equationi(ctx, buf, mode, advanced_mode);
}
@ -606,7 +607,7 @@ void GLAPIENTRY
_mesa_BlendEquationiARB(GLuint buf, GLenum mode)
{
GET_CURRENT_CONTEXT(ctx);
enum gl_advanced_blend_mode advanced_mode = advanced_blend_mode(ctx, mode);
enum pipe_advanced_blend_mode advanced_mode = advanced_blend_mode(ctx, mode);
if (MESA_VERBOSE & VERBOSE_API)
_mesa_debug(ctx, "glBlendEquationi(%u, %s)\n",
@ -687,7 +688,7 @@ blend_equation_separate(struct gl_context *ctx, GLenum modeRGB, GLenum modeA,
ctx->Color.Blend[buf].EquationA = modeA;
}
ctx->Color._BlendEquationPerBuffer = GL_FALSE;
set_advanced_blend_mode(ctx, BLEND_NONE);
set_advanced_blend_mode(ctx, PIPE_ADVANCED_BLEND_NONE);
}
@ -743,7 +744,7 @@ blend_equation_separatei(struct gl_context *ctx, GLuint buf, GLenum modeRGB,
ctx->Color.Blend[buf].EquationRGB = modeRGB;
ctx->Color.Blend[buf].EquationA = modeA;
ctx->Color._BlendEquationPerBuffer = GL_TRUE;
set_advanced_blend_mode(ctx, BLEND_NONE);
set_advanced_blend_mode(ctx, PIPE_ADVANCED_BLEND_NONE);
}
@ -824,7 +825,7 @@ _mesa_BlendColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha )
* \param func alpha comparison function.
* \param ref reference value.
*
* Verifies the parameters and updates gl_colorbuffer_attrib.
* Verifies the parameters and updates gl_colorbuffer_attrib.
* On a change, flushes the vertices and notifies the driver via
* dd_function_table::AlphaFunc callback.
*/

View file

@ -33,6 +33,7 @@
#define BLEND_H
#include "util/blend.h"
#include "util/glheader.h"
#include "context.h"
#include "formats.h"
@ -63,21 +64,21 @@ extern void
_mesa_update_clamp_vertex_color(struct gl_context *ctx,
const struct gl_framebuffer *drawFb);
extern void
extern void
_mesa_init_color( struct gl_context * ctx );
static inline enum gl_advanced_blend_mode
static inline enum pipe_advanced_blend_mode
_mesa_get_advanced_blend_sh_constant(GLbitfield blend_enabled,
enum gl_advanced_blend_mode mode)
enum pipe_advanced_blend_mode mode)
{
return blend_enabled ? mode : BLEND_NONE;
return blend_enabled ? mode : PIPE_ADVANCED_BLEND_NONE;
}
static inline bool
_mesa_advanded_blend_sh_constant_changed(struct gl_context *ctx,
GLbitfield new_blend_enabled,
enum gl_advanced_blend_mode new_mode)
enum pipe_advanced_blend_mode new_mode)
{
return _mesa_get_advanced_blend_sh_constant(new_blend_enabled, new_mode) !=
_mesa_get_advanced_blend_sh_constant(ctx->Color.BlendEnabled,
@ -94,7 +95,7 @@ _mesa_flush_vertices_for_blend_state(struct gl_context *ctx)
static inline void
_mesa_flush_vertices_for_blend_adv(struct gl_context *ctx,
GLbitfield new_blend_enabled,
enum gl_advanced_blend_mode new_mode)
enum pipe_advanced_blend_mode new_mode)
{
/* The advanced blend mode needs _NEW_COLOR to update the state constant. */
if (_mesa_has_KHR_blend_equation_advanced(ctx) &&

View file

@ -121,7 +121,7 @@ _mesa_update_valid_to_render_state(struct gl_context *ctx)
return;
if (ctx->Color.BlendEnabled &&
ctx->Color._AdvancedBlendMode != BLEND_NONE) {
ctx->Color._AdvancedBlendMode != PIPE_ADVANCED_BLEND_NONE) {
/* The KHR_blend_equation_advanced spec says:
*
* "If any non-NONE draw buffer uses a blend equation found in table

View file

@ -292,7 +292,7 @@ struct gl_colorbuffer_attrib
* draw buffer, and NVX_blend_equation_advanced_multi_draw_buffer still
* requires all draw buffers to match, so we only need a single value.
*/
enum gl_advanced_blend_mode _AdvancedBlendMode;
enum pipe_advanced_blend_mode _AdvancedBlendMode;
/** Coherency requested via glEnable(GL_BLEND_ADVANCED_COHERENT_KHR)? */
bool BlendCoherent;

View file

@ -262,7 +262,7 @@ st_update_blend( struct st_context *st )
blend->rt[i].colormask = colormask;
}
if (ctx->Color._AdvancedBlendMode != BLEND_NONE) {
if (ctx->Color._AdvancedBlendMode != PIPE_ADVANCED_BLEND_NONE) {
blend->blend_coherent = ctx->Color.BlendCoherent;
}
@ -272,11 +272,11 @@ st_update_blend( struct st_context *st )
blend->logicop_func = ctx->Color._LogicOp;
}
else if (ctx->Color.BlendEnabled &&
ctx->Color._AdvancedBlendMode != BLEND_NONE) {
ctx->Color._AdvancedBlendMode != PIPE_ADVANCED_BLEND_NONE) {
blend->advanced_blend_func = ctx->Color._AdvancedBlendMode;
}
else if (ctx->Color.BlendEnabled &&
ctx->Color._AdvancedBlendMode == BLEND_NONE) {
ctx->Color._AdvancedBlendMode == PIPE_ADVANCED_BLEND_NONE) {
/* blending enabled */
for (i = 0, j = 0; i < num_state; i++) {
if (!(ctx->Color.BlendEnabled & (1 << i)) ||

View file

@ -78,6 +78,57 @@ enum pipe_logicop {
PIPE_LOGICOP_SET,
};
enum pipe_advanced_blend_mode {
PIPE_ADVANCED_BLEND_NONE = 0,
PIPE_ADVANCED_BLEND_MULTIPLY,
PIPE_ADVANCED_BLEND_SCREEN,
PIPE_ADVANCED_BLEND_OVERLAY,
PIPE_ADVANCED_BLEND_DARKEN,
PIPE_ADVANCED_BLEND_LIGHTEN,
PIPE_ADVANCED_BLEND_COLORDODGE,
PIPE_ADVANCED_BLEND_COLORBURN,
PIPE_ADVANCED_BLEND_HARDLIGHT,
PIPE_ADVANCED_BLEND_SOFTLIGHT,
PIPE_ADVANCED_BLEND_DIFFERENCE,
PIPE_ADVANCED_BLEND_EXCLUSION,
PIPE_ADVANCED_BLEND_HSL_HUE,
PIPE_ADVANCED_BLEND_HSL_SATURATION,
PIPE_ADVANCED_BLEND_HSL_COLOR,
PIPE_ADVANCED_BLEND_HSL_LUMINOSITY,
/* extra Vulkan only */
PIPE_ADVANCED_BLEND_SRC,
PIPE_ADVANCED_BLEND_DST,
PIPE_ADVANCED_BLEND_SRC_OVER,
PIPE_ADVANCED_BLEND_DST_OVER,
PIPE_ADVANCED_BLEND_SRC_IN,
PIPE_ADVANCED_BLEND_DST_IN,
PIPE_ADVANCED_BLEND_SRC_OUT,
PIPE_ADVANCED_BLEND_DST_OUT,
PIPE_ADVANCED_BLEND_SRC_ATOP,
PIPE_ADVANCED_BLEND_DST_ATOP,
PIPE_ADVANCED_BLEND_XOR,
PIPE_ADVANCED_BLEND_INVERT,
PIPE_ADVANCED_BLEND_INVERT_RGB,
PIPE_ADVANCED_BLEND_LINEARDODGE,
PIPE_ADVANCED_BLEND_LINEARBURN,
PIPE_ADVANCED_BLEND_VIVIDLIGHT,
PIPE_ADVANCED_BLEND_LINEARLIGHT,
PIPE_ADVANCED_BLEND_PINLIGHT,
PIPE_ADVANCED_BLEND_HARDMIX,
PIPE_ADVANCED_BLEND_PLUS,
PIPE_ADVANCED_BLEND_PLUS_CLAMPED,
PIPE_ADVANCED_BLEND_PLUS_CLAMPED_ALPHA,
PIPE_ADVANCED_BLEND_PLUS_DARKER,
PIPE_ADVANCED_BLEND_MINUS,
PIPE_ADVANCED_BLEND_MINUS_CLAMPED,
PIPE_ADVANCED_BLEND_CONTRAST,
PIPE_ADVANCED_BLEND_INVERT_OVG,
PIPE_ADVANCED_BLEND_RED,
PIPE_ADVANCED_BLEND_GREEN,
PIPE_ADVANCED_BLEND_BLUE,
};
/**
* When faking RGBX render target formats with RGBA ones, the blender is still
* supposed to treat the destination's alpha channel as 1 instead of the

View file

@ -6,6 +6,7 @@
#include "vk_blend.h"
#include "util/macros.h"
#include "vulkan/vulkan_core.h"
enum pipe_logicop
vk_logic_op_to_pipe(VkLogicOp in)
@ -113,3 +114,104 @@ vk_blend_factor_to_pipe(VkBlendFactor vk_factor)
UNREACHABLE("Invalid blend factor");
}
}
enum pipe_advanced_blend_mode
vk_advanced_blend_op_to_pipe(VkBlendOp in)
{
switch (in) {
case VK_BLEND_OP_ZERO_EXT:
return PIPE_ADVANCED_BLEND_NONE;
case VK_BLEND_OP_MULTIPLY_EXT:
return PIPE_ADVANCED_BLEND_MULTIPLY;
case VK_BLEND_OP_SCREEN_EXT:
return PIPE_ADVANCED_BLEND_SCREEN;
case VK_BLEND_OP_OVERLAY_EXT:
return PIPE_ADVANCED_BLEND_OVERLAY;
case VK_BLEND_OP_DARKEN_EXT:
return PIPE_ADVANCED_BLEND_DARKEN;
case VK_BLEND_OP_LIGHTEN_EXT:
return PIPE_ADVANCED_BLEND_LIGHTEN;
case VK_BLEND_OP_COLORDODGE_EXT:
return PIPE_ADVANCED_BLEND_COLORDODGE;
case VK_BLEND_OP_COLORBURN_EXT:
return PIPE_ADVANCED_BLEND_COLORBURN;
case VK_BLEND_OP_HARDLIGHT_EXT:
return PIPE_ADVANCED_BLEND_HARDLIGHT;
case VK_BLEND_OP_SOFTLIGHT_EXT:
return PIPE_ADVANCED_BLEND_SOFTLIGHT;
case VK_BLEND_OP_DIFFERENCE_EXT:
return PIPE_ADVANCED_BLEND_DIFFERENCE;
case VK_BLEND_OP_EXCLUSION_EXT:
return PIPE_ADVANCED_BLEND_EXCLUSION;
case VK_BLEND_OP_HSL_HUE_EXT:
return PIPE_ADVANCED_BLEND_HSL_HUE;
case VK_BLEND_OP_HSL_SATURATION_EXT:
return PIPE_ADVANCED_BLEND_HSL_SATURATION;
case VK_BLEND_OP_HSL_COLOR_EXT:
return PIPE_ADVANCED_BLEND_HSL_COLOR;
case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
return PIPE_ADVANCED_BLEND_HSL_LUMINOSITY;
case VK_BLEND_OP_SRC_EXT:
return PIPE_ADVANCED_BLEND_SRC;
case VK_BLEND_OP_DST_EXT:
return PIPE_ADVANCED_BLEND_DST;
case VK_BLEND_OP_SRC_OVER_EXT:
return PIPE_ADVANCED_BLEND_SRC_OVER;
case VK_BLEND_OP_DST_OVER_EXT:
return PIPE_ADVANCED_BLEND_DST_OVER;
case VK_BLEND_OP_SRC_IN_EXT:
return PIPE_ADVANCED_BLEND_SRC_IN;
case VK_BLEND_OP_DST_IN_EXT:
return PIPE_ADVANCED_BLEND_DST_IN;
case VK_BLEND_OP_SRC_OUT_EXT:
return PIPE_ADVANCED_BLEND_SRC_OUT;
case VK_BLEND_OP_DST_OUT_EXT:
return PIPE_ADVANCED_BLEND_DST_OUT;
case VK_BLEND_OP_SRC_ATOP_EXT:
return PIPE_ADVANCED_BLEND_SRC_ATOP;
case VK_BLEND_OP_DST_ATOP_EXT:
return PIPE_ADVANCED_BLEND_DST_ATOP;
case VK_BLEND_OP_XOR_EXT:
return PIPE_ADVANCED_BLEND_XOR;
case VK_BLEND_OP_INVERT_EXT:
return PIPE_ADVANCED_BLEND_INVERT;
case VK_BLEND_OP_INVERT_RGB_EXT:
return PIPE_ADVANCED_BLEND_INVERT_RGB;
case VK_BLEND_OP_LINEARDODGE_EXT:
return PIPE_ADVANCED_BLEND_LINEARDODGE;
case VK_BLEND_OP_LINEARBURN_EXT:
return PIPE_ADVANCED_BLEND_LINEARBURN;
case VK_BLEND_OP_VIVIDLIGHT_EXT:
return PIPE_ADVANCED_BLEND_VIVIDLIGHT;
case VK_BLEND_OP_LINEARLIGHT_EXT:
return PIPE_ADVANCED_BLEND_LINEARLIGHT;
case VK_BLEND_OP_PINLIGHT_EXT:
return PIPE_ADVANCED_BLEND_PINLIGHT;
case VK_BLEND_OP_HARDMIX_EXT:
return PIPE_ADVANCED_BLEND_HARDMIX;
case VK_BLEND_OP_PLUS_EXT:
return PIPE_ADVANCED_BLEND_PLUS;
case VK_BLEND_OP_PLUS_CLAMPED_EXT:
return PIPE_ADVANCED_BLEND_PLUS_CLAMPED;
case VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT:
return PIPE_ADVANCED_BLEND_PLUS_CLAMPED_ALPHA;
case VK_BLEND_OP_PLUS_DARKER_EXT:
return PIPE_ADVANCED_BLEND_PLUS_DARKER;
case VK_BLEND_OP_MINUS_EXT:
return PIPE_ADVANCED_BLEND_MINUS;
case VK_BLEND_OP_MINUS_CLAMPED_EXT:
return PIPE_ADVANCED_BLEND_MINUS_CLAMPED;
case VK_BLEND_OP_CONTRAST_EXT:
return PIPE_ADVANCED_BLEND_CONTRAST;
case VK_BLEND_OP_INVERT_OVG_EXT:
return PIPE_ADVANCED_BLEND_INVERT_OVG;
case VK_BLEND_OP_RED_EXT:
return PIPE_ADVANCED_BLEND_RED;
case VK_BLEND_OP_GREEN_EXT:
return PIPE_ADVANCED_BLEND_GREEN;
case VK_BLEND_OP_BLUE_EXT:
return PIPE_ADVANCED_BLEND_BLUE;
default:
UNREACHABLE("Invalid advanced blend op");
}
}

View file

@ -17,6 +17,7 @@ extern "C" {
enum pipe_logicop vk_logic_op_to_pipe(VkLogicOp in);
enum pipe_blend_func vk_blend_op_to_pipe(VkBlendOp in);
enum pipe_blendfactor vk_blend_factor_to_pipe(VkBlendFactor in);
enum pipe_advanced_blend_mode vk_advanced_blend_op_to_pipe(VkBlendOp in);
#ifdef __cplusplus
}

View file

@ -127,6 +127,7 @@ get_dynamic_state_groups(BITSET_WORD *dynamic,
BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_BLEND_EQUATIONS);
BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_WRITE_MASKS);
BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_BLEND_CONSTANTS);
BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_BLEND_ADVANCED);
}
if (groups & MESA_VK_GRAPHICS_STATE_COLOR_ATTACHMENT_MAP_BIT)
@ -177,7 +178,8 @@ fully_dynamic_state_groups(const BITSET_WORD *dynamic)
BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_ENABLES) &&
BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_EQUATIONS) &&
BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_WRITE_MASKS) &&
BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_CONSTANTS))
BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_CONSTANTS) &&
BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_ADVANCED))
groups |= MESA_VK_GRAPHICS_STATE_COLOR_BLEND_BIT;
if (BITSET_TEST(dynamic, MESA_VK_DYNAMIC_COLOR_ATTACHMENT_MAP))
@ -295,18 +297,17 @@ vk_get_dynamic_graphics_states(BITSET_WORD *dynamic,
CASE( DEPTH_CLIP_NEGATIVE_ONE_TO_ONE_EXT, VP_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE)
CASE( ATTACHMENT_FEEDBACK_LOOP_ENABLE_EXT, ATTACHMENT_FEEDBACK_LOOP_ENABLE)
CASE( DEPTH_CLAMP_RANGE_EXT, VP_DEPTH_CLAMP_RANGE)
CASE( COLOR_BLEND_ADVANCED_EXT, CB_BLEND_ADVANCED)
default:
UNREACHABLE("Unsupported dynamic graphics state");
}
}
/* attachmentCount is ignored if all of the states using it are dyanmic.
*
* TODO: Handle advanced blending here when supported.
*/
/* attachmentCount is ignored if all of the states using it are dyanmic. */
if (BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_ENABLES) &&
BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_EQUATIONS) &&
BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_WRITE_MASKS))
BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_WRITE_MASKS) &&
BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_ADVANCED))
BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_ATTACHMENT_COUNT);
}
@ -1043,6 +1044,24 @@ vk_color_blend_state_init(struct vk_color_blend_state *cb,
} else {
cb->color_write_enables = BITFIELD_MASK(MESA_VK_MAX_COLOR_ATTACHMENTS);
}
const VkPipelineColorBlendAdvancedStateCreateInfoEXT *advanced =
vk_find_struct_const(cb_info->pNext, PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT);
if (advanced) {
/* Propagate global advanced blend state to per-attachment state */
for (uint32_t a = 0; a < cb->attachment_count; a++) {
cb->attachments[a].src_premultiplied = advanced->srcPremultiplied;
cb->attachments[a].dst_premultiplied = advanced->dstPremultiplied;
cb->attachments[a].blend_overlap = advanced->blendOverlap;
}
} else {
/* Vulkan spec defaults */
for (uint32_t a = 0; a < cb->attachment_count; a++) {
cb->attachments[a].src_premultiplied = true;
cb->attachments[a].dst_premultiplied = true;
cb->attachments[a].blend_overlap = VK_BLEND_OVERLAP_UNCORRELATED_EXT;
}
}
}
/* From the description of VkRenderingInputAttachmentIndexInfoKHR:
@ -1128,7 +1147,8 @@ vk_dynamic_graphics_state_init_cb(struct vk_dynamic_graphics_state *dst,
if (IS_NEEDED(CB_BLEND_ENABLES) ||
IS_NEEDED(CB_BLEND_EQUATIONS) ||
IS_NEEDED(CB_WRITE_MASKS)) {
IS_NEEDED(CB_WRITE_MASKS) ||
IS_NEEDED(CB_BLEND_ADVANCED)) {
typed_memcpy(dst->cb.attachments, cb->attachments, cb->attachment_count);
}
@ -2263,6 +2283,17 @@ vk_dynamic_graphics_state_copy(struct vk_dynamic_graphics_state *dst,
if (IS_SET_IN_SRC(CB_BLEND_CONSTANTS))
COPY_ARRAY(CB_BLEND_CONSTANTS, cb.blend_constants, 4);
if (IS_SET_IN_SRC(CB_BLEND_ADVANCED)) {
for (uint32_t a = 0; a < src->cb.attachment_count; a++) {
COPY_MEMBER(CB_BLEND_ADVANCED,
cb.attachments[a].dst_premultiplied);
COPY_MEMBER(CB_BLEND_ADVANCED,
cb.attachments[a].src_premultiplied);
COPY_MEMBER(CB_BLEND_ADVANCED,
cb.attachments[a].blend_overlap);
}
}
COPY_IF_SET(RP_ATTACHMENTS, rp.attachments);
if (IS_SET_IN_SRC(INPUT_ATTACHMENT_MAP)) {
@ -3085,7 +3116,37 @@ vk_common_CmdSetColorBlendAdvancedEXT(VkCommandBuffer commandBuffer,
uint32_t attachmentCount,
const VkColorBlendAdvancedEXT* pColorBlendAdvanced)
{
UNREACHABLE("VK_EXT_blend_operation_advanced unsupported");
VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer);
struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state;
for (uint32_t i = 0; i < attachmentCount; i++) {
uint32_t a = firstAttachment + i;
assert(a < ARRAY_SIZE(dyn->cb.attachments));
SET_DYN_VALUE(dyn, CB_BLEND_ADVANCED,
cb.attachments[a].color_blend_op,
pColorBlendAdvanced[i].advancedBlendOp);
SET_DYN_VALUE(dyn, CB_BLEND_ADVANCED,
cb.attachments[a].alpha_blend_op,
pColorBlendAdvanced[i].advancedBlendOp);
SET_DYN_VALUE(dyn, CB_BLEND_ADVANCED,
cb.attachments[a].src_premultiplied,
pColorBlendAdvanced[i].srcPremultiplied);
SET_DYN_VALUE(dyn, CB_BLEND_ADVANCED,
cb.attachments[a].dst_premultiplied,
pColorBlendAdvanced[i].dstPremultiplied);
SET_DYN_VALUE(dyn, CB_BLEND_ADVANCED,
cb.attachments[a].blend_overlap,
pColorBlendAdvanced[i].blendOverlap);
SET_DYN_VALUE(dyn, CB_BLEND_ADVANCED,
cb.attachments[a].clamp_results,
pColorBlendAdvanced[i].clampResults);
}
}
void
@ -3382,6 +3443,7 @@ vk_dynamic_graphic_state_to_str(enum mesa_vk_dynamic_graphics_state state)
NAME(CB_BLEND_CONSTANTS);
NAME(ATTACHMENT_FEEDBACK_LOOP_ENABLE);
NAME(COLOR_ATTACHMENT_MAP);
NAME(CB_BLEND_ADVANCED);
default: UNREACHABLE("Invalid state");
}

View file

@ -108,6 +108,7 @@ enum mesa_vk_dynamic_graphics_state {
MESA_VK_DYNAMIC_ATTACHMENT_FEEDBACK_LOOP_ENABLE,
MESA_VK_DYNAMIC_COLOR_ATTACHMENT_MAP,
MESA_VK_DYNAMIC_INPUT_ATTACHMENT_MAP,
MESA_VK_DYNAMIC_CB_BLEND_ADVANCED,
/* Must be left at the end */
MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX,
@ -656,6 +657,15 @@ struct vk_color_blend_attachment_state {
* MESA_VK_DYNAMIC_CB_BLEND_EQUATIONS
*/
VkBlendOp alpha_blend_op;
/** VkColorBlendAdvancedEXT - advanced blend parameters
*
* MESA_VK_DYNAMIC_CB_BLEND_ADVANCED
*/
bool src_premultiplied;
bool dst_premultiplied;
VkBlendOverlapEXT blend_overlap;
bool clamp_results;
};
/***/