diff --git a/docs/features.txt b/docs/features.txt index 5e907be8ff9..dfa0e65359e 100644 --- a/docs/features.txt +++ b/docs/features.txt @@ -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+) diff --git a/src/asahi/lib/agx_linker.h b/src/asahi/lib/agx_linker.h index 9532fa6b80f..53235d83560 100644 --- a/src/asahi/lib/agx_linker.h +++ b/src/asahi/lib/agx_linker.h @@ -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]; diff --git a/src/asahi/lib/agx_nir_prolog_epilog.c b/src/asahi/lib/agx_nir_prolog_epilog.c index 34d13f35a9b..e76a2bdf8e2 100644 --- a/src/asahi/lib/agx_nir_prolog_epilog.c +++ b/src/asahi/lib/agx_nir_prolog_epilog.c @@ -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 diff --git a/src/asahi/vulkan/hk_cmd_draw.c b/src/asahi/vulkan/hk_cmd_draw.c index bcbec0a0fac..709644d020c 100644 --- a/src/asahi/vulkan/hk_cmd_draw.c +++ b/src/asahi/vulkan/hk_cmd_draw.c @@ -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)), }; } } diff --git a/src/asahi/vulkan/hk_physical_device.c b/src/asahi/vulkan/hk_physical_device.c index c49fac86e21..8aaa3a5c52a 100644 --- a/src/asahi/vulkan/hk_physical_device.c +++ b/src/asahi/vulkan/hk_physical_device.c @@ -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)); diff --git a/src/compiler/glsl/gl_nir_lower_blend_equation_advanced.c b/src/compiler/glsl/gl_nir_lower_blend_equation_advanced.c index e7cd2ba9ab2..daba92d50f5 100644 --- a/src/compiler/glsl/gl_nir_lower_blend_equation_advanced.c +++ b/src/compiler/glsl/gl_nir_lower_blend_equation_advanced.c @@ -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) diff --git a/src/compiler/glsl/glsl_parser.yy b/src/compiler/glsl/glsl_parser.yy index edb34bd0f03..996aec41ef3 100644 --- a/src/compiler/glsl/glsl_parser.yy +++ b/src/compiler/glsl/glsl_parser.yy @@ -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) { diff --git a/src/compiler/nir/nir_blend_equation_advanced_helper.h b/src/compiler/nir/nir_blend_equation_advanced_helper.h new file mode 100644 index 00000000000..8c59a13298b --- /dev/null +++ b/src/compiler/nir/nir_blend_equation_advanced_helper.h @@ -0,0 +1,132 @@ +/* + * Copyright © 2023 Timothy Arceri + * 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)))); +} diff --git a/src/compiler/nir/nir_lower_blend.c b/src/compiler/nir/nir_lower_blend.c index a549cd6a386..c65f69da4f6 100644 --- a/src/compiler/nir/nir_lower_blend.c +++ b/src/compiler/nir/nir_lower_blend.c @@ -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)); diff --git a/src/compiler/nir/nir_lower_blend.h b/src/compiler/nir/nir_lower_blend.h index 5e3fa11b41f..642934bad7e 100644 --- a/src/compiler/nir/nir_lower_blend.h +++ b/src/compiler/nir/nir_lower_blend.h @@ -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 { diff --git a/src/compiler/shader_enums.h b/src/compiler/shader_enums.h index e13c18a0089..de5c7957673 100644 --- a/src/compiler/shader_enums.h +++ b/src/compiler/shader_enums.h @@ -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, diff --git a/src/mesa/main/blend.c b/src/mesa/main/blend.c index 43a75d21232..ab9d57473b2 100644 --- a/src/mesa/main/blend.c +++ b/src/mesa/main/blend.c @@ -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. */ diff --git a/src/mesa/main/blend.h b/src/mesa/main/blend.h index 69f8465c1a0..8071ab33b8b 100644 --- a/src/mesa/main/blend.h +++ b/src/mesa/main/blend.h @@ -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) && diff --git a/src/mesa/main/draw_validate.c b/src/mesa/main/draw_validate.c index bc69fad5c9e..a73fc2a661a 100644 --- a/src/mesa/main/draw_validate.c +++ b/src/mesa/main/draw_validate.c @@ -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 diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index 66d890cb89d..f0ee58059ca 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -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; diff --git a/src/mesa/state_tracker/st_atom_blend.c b/src/mesa/state_tracker/st_atom_blend.c index 94fea8ea3af..75ebf9a0015 100644 --- a/src/mesa/state_tracker/st_atom_blend.c +++ b/src/mesa/state_tracker/st_atom_blend.c @@ -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)) || diff --git a/src/util/blend.h b/src/util/blend.h index d95cadaffdf..87c8c29fec4 100644 --- a/src/util/blend.h +++ b/src/util/blend.h @@ -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 diff --git a/src/vulkan/runtime/vk_blend.c b/src/vulkan/runtime/vk_blend.c index dd70142a8c7..11769fcbc66 100644 --- a/src/vulkan/runtime/vk_blend.c +++ b/src/vulkan/runtime/vk_blend.c @@ -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"); + } +} diff --git a/src/vulkan/runtime/vk_blend.h b/src/vulkan/runtime/vk_blend.h index fb50c17eddc..49858f114d0 100644 --- a/src/vulkan/runtime/vk_blend.h +++ b/src/vulkan/runtime/vk_blend.h @@ -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 } diff --git a/src/vulkan/runtime/vk_graphics_state.c b/src/vulkan/runtime/vk_graphics_state.c index 2c6415ee796..33d8aa2b9a9 100644 --- a/src/vulkan/runtime/vk_graphics_state.c +++ b/src/vulkan/runtime/vk_graphics_state.c @@ -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"); } diff --git a/src/vulkan/runtime/vk_graphics_state.h b/src/vulkan/runtime/vk_graphics_state.h index 8713759df39..36b9bba8205 100644 --- a/src/vulkan/runtime/vk_graphics_state.h +++ b/src/vulkan/runtime/vk_graphics_state.h @@ -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; }; /***/