diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 3765e9660..2613fbd52 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -262,6 +262,26 @@ vk::BlendOp BlendOp(AmdGpu::BlendControl::BlendFunc func) { } } +void NormalizeMinMaxBlend(vk::BlendOp& op, vk::BlendFactor& src, vk::BlendFactor& dst, + AmdGpu::NumberFormat format) { + if (format != AmdGpu::NumberFormat::Unorm && format != AmdGpu::NumberFormat::Srgb) { + return; + } + if (op != vk::BlendOp::eMin && op != vk::BlendOp::eMax) { + return; + } + if ((src != vk::BlendFactor::eZero && src != vk::BlendFactor::eOne) || + (dst != vk::BlendFactor::eZero && dst != vk::BlendFactor::eOne) || + (src != vk::BlendFactor::eZero && dst != vk::BlendFactor::eZero)) { + return; + } + if (op == vk::BlendOp::eMin) { + src = vk::BlendFactor::eZero; + dst = vk::BlendFactor::eZero; + } + op = vk::BlendOp::eAdd; +} + vk::LogicOp LogicOp(AmdGpu::ColorControl::LogicOp logic_op) { using LogicOp = AmdGpu::ColorControl::LogicOp; switch (logic_op) { diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index cb4ba527c..6f9260f36 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -35,6 +35,9 @@ bool IsDualSourceBlendFactor(AmdGpu::BlendControl::BlendFactor factor); vk::BlendOp BlendOp(AmdGpu::BlendControl::BlendFunc func); +void NormalizeMinMaxBlend(vk::BlendOp& op, vk::BlendFactor& src, vk::BlendFactor& dst, + AmdGpu::NumberFormat format); + vk::LogicOp LogicOp(AmdGpu::ColorControl::LogicOp logic_op); vk::SamplerAddressMode ClampMode(AmdGpu::ClampMode mode); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 307ff5ca1..d5093ce57 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -311,47 +311,14 @@ GraphicsPipeline::GraphicsPipeline( const auto alpha_blend = control.separate_alpha_blend ? LiverpoolToVK::BlendOp(control.alpha_func) : color_blend; - // Vulkan ignores blend factors for min/max, but a factor that zeroes one operand - // makes the operation collapse to a plain selection: min(s, 0) is 0 and max(s, 0) - // is s for normalized alpha. Rewrite those to the equivalent add so the result is - // exact instead of leaving the other operand to survive. - auto eff_src_alpha = src_alpha; - auto eff_dst_alpha = dst_alpha; - auto eff_alpha_blend = alpha_blend; - if (alpha_blend == vk::BlendOp::eMin || alpha_blend == vk::BlendOp::eMax) { - const bool takes_max = alpha_blend == vk::BlendOp::eMax; - if (src_alpha == vk::BlendFactor::eOne && dst_alpha == vk::BlendFactor::eZero) { - eff_alpha_blend = vk::BlendOp::eAdd; - eff_src_alpha = takes_max ? vk::BlendFactor::eOne : vk::BlendFactor::eZero; - eff_dst_alpha = vk::BlendFactor::eZero; - } else if (src_alpha == vk::BlendFactor::eZero && dst_alpha == vk::BlendFactor::eOne) { - eff_alpha_blend = vk::BlendOp::eAdd; - eff_src_alpha = vk::BlendFactor::eZero; - eff_dst_alpha = takes_max ? vk::BlendFactor::eOne : vk::BlendFactor::eZero; - } - } - - const auto color_scaled_min_max = - (color_blend == vk::BlendOp::eMin || color_blend == vk::BlendOp::eMax) && - (src_color != vk::BlendFactor::eOne || dst_color != vk::BlendFactor::eOne) && - !key.color_buffers[i].blend_self_scale; - const auto alpha_scaled_min_max = - (eff_alpha_blend == vk::BlendOp::eMin || eff_alpha_blend == vk::BlendOp::eMax) && - (eff_src_alpha != vk::BlendFactor::eOne || eff_dst_alpha != vk::BlendFactor::eOne); - if (color_scaled_min_max || alpha_scaled_min_max) { - LOG_WARNING( - Render_Vulkan, - "Unimplemented use of min/max blend op with blend factor not equal to one."); - } - attachments[i] = vk::PipelineColorBlendAttachmentState{ .blendEnable = control.enable, .srcColorBlendFactor = src_color, .dstColorBlendFactor = dst_color, .colorBlendOp = color_blend, - .srcAlphaBlendFactor = eff_src_alpha, - .dstAlphaBlendFactor = eff_dst_alpha, - .alphaBlendOp = eff_alpha_blend, + .srcAlphaBlendFactor = src_alpha, + .dstAlphaBlendFactor = dst_alpha, + .alphaBlendOp = alpha_blend, .colorWriteMask = instance.IsDynamicColorWriteMaskSupported() ? vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | @@ -391,6 +358,30 @@ GraphicsPipeline::GraphicsPipeline( ? vk::BlendFactor::eOne : vk::BlendFactor::eZero; // 1-A } + + auto& attachment = attachments[i]; + LiverpoolToVK::NormalizeMinMaxBlend(attachment.colorBlendOp, attachment.srcColorBlendFactor, + attachment.dstColorBlendFactor, + key.color_buffers[i].num_format); + LiverpoolToVK::NormalizeMinMaxBlend(attachment.alphaBlendOp, attachment.srcAlphaBlendFactor, + attachment.dstAlphaBlendFactor, + key.color_buffers[i].num_format); + + const auto color_scaled_min_max = + (attachment.colorBlendOp == vk::BlendOp::eMin || + attachment.colorBlendOp == vk::BlendOp::eMax) && + (attachment.srcColorBlendFactor != vk::BlendFactor::eOne || + attachment.dstColorBlendFactor != vk::BlendFactor::eOne); + const auto alpha_scaled_min_max = + (attachment.alphaBlendOp == vk::BlendOp::eMin || + attachment.alphaBlendOp == vk::BlendOp::eMax) && + (attachment.srcAlphaBlendFactor != vk::BlendFactor::eOne || + attachment.dstAlphaBlendFactor != vk::BlendFactor::eOne); + if (control.enable && (color_scaled_min_max || alpha_scaled_min_max)) { + LOG_WARNING( + Render_Vulkan, + "Unimplemented use of min/max blend op with blend factor not equal to one."); + } } const vk::PipelineColorBlendStateCreateInfo color_blending = {