diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 6800c20f69..56b65c527c 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -359,6 +359,9 @@ void RestoreGlobalState(bool is_powered_on) { for (const auto& reset : values.linkage.restore_functions) { reset(); } + + // Reset per-game flags + values.use_squashed_iterated_blend = false; } static bool configuring_global = true; diff --git a/src/common/settings.h b/src/common/settings.h index 360a49c6c6..2e16e4bc59 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -759,6 +759,9 @@ struct Values { // Add-Ons std::map> disabled_addons; + + // Per-game overrides + bool use_squashed_iterated_blend; }; extern Values values; diff --git a/src/core/core.cpp b/src/core/core.cpp index 7315f35e0c..6c321afdbb 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -297,6 +297,9 @@ struct System::Impl { std::string vendor = gpu_core->Renderer().GetDeviceVendor(); LOG_INFO(Core, "GPU Vendor: {}", vendor); + // Reset all per-game flags + Settings::values.use_squashed_iterated_blend = false; + // Insert PC overrides here #ifdef ANDROID @@ -322,6 +325,13 @@ struct System::Impl { #endif + // Ninja Gaiden Ragebound + constexpr u64 ngr = 0x0100781020710000ULL; + + if (programId == ngr) { + LOG_INFO(Core, "Enabling game specifc override: use_squashed_iterated_blend"); + Settings::values.use_squashed_iterated_blend = true; + } } SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, @@ -425,6 +435,9 @@ struct System::Impl { void ShutdownMainProcess() { SetShuttingDown(true); + // Reset per-game flags + Settings::values.use_squashed_iterated_blend = false; + is_powered_on = false; exit_locked = false; exit_requested = false; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 718958fc11..6f25267a8f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -1142,6 +1142,14 @@ void RasterizerOpenGL::SyncBlendState() { glDisable(GL_BLEND); return; } + // Temporary workaround for games that use iterated blending + if (regs.iterated_blend.enable && Settings::values.use_squashed_iterated_blend) { + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + return; + } + glEnable(GL_BLEND); glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.color_source), MaxwellToGL::BlendFunc(regs.blend.color_dest), diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 453a3d942b..e643e98ead 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -11,6 +11,7 @@ #include #include "common/cityhash.h" #include "common/common_types.h" +#include "common/settings.h" #include "video_core/engines/draw_manager.h" #include "video_core/renderer_vulkan/fixed_pipeline_state.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" @@ -201,6 +202,19 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t }; if (!regs.blend_per_target_enabled) { + // Temporary workaround for games that use iterated blending + // even when dynamic blending is off so overrides work with EDS = 0 as well + if (regs.iterated_blend.enable && Settings::values.use_squashed_iterated_blend) { + equation_rgb.Assign(PackBlendEquation(Maxwell::Blend::Equation::Add_GL)); + equation_a.Assign(PackBlendEquation(Maxwell::Blend::Equation::Add_GL)); + factor_source_rgb.Assign(PackBlendFactor(Maxwell::Blend::Factor::One_GL)); + factor_dest_rgb.Assign(PackBlendFactor(Maxwell::Blend::Factor::One_GL)); + factor_source_a.Assign( + PackBlendFactor(Maxwell::Blend::Factor::OneMinusSourceColor_GL)); + factor_dest_a.Assign(PackBlendFactor(Maxwell::Blend::Factor::Zero_GL)); + enable.Assign(1); + return; + } setup_blend(regs.blend); return; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 530c7e8e41..134327fa8d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1538,22 +1538,41 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) { if (state_tracker.TouchBlendEquations()) { std::array setup_blends{}; - for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { - const auto blend_setup = [&](const T& guest_blend) { - auto& host_blend = setup_blends[index]; - host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source); - host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest); - host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op); - host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source); - host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest); - host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op); - }; - if (!regs.blend_per_target_enabled) { - blend_setup(regs.blend); - continue; + + const auto blend_setup = [&](auto& host_blend, const auto& guest_blend) { + host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source); + host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest); + host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op); + host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source); + host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest); + host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op); + }; + + // Single blend equation for all targets + if (!regs.blend_per_target_enabled) { + // Temporary workaround for games that use iterated blending + if (regs.iterated_blend.enable && Settings::values.use_squashed_iterated_blend) { + setup_blends[0].srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + setup_blends[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + setup_blends[0].colorBlendOp = VK_BLEND_OP_ADD; + setup_blends[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + setup_blends[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + setup_blends[0].alphaBlendOp = VK_BLEND_OP_ADD; + } else { + blend_setup(setup_blends[0], regs.blend); + } + + // Copy first blend state to all other targets + for (size_t index = 1; index < Maxwell::NumRenderTargets; index++) { + setup_blends[index] = setup_blends[0]; + } + } else { + // Per-target blending + for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { + blend_setup(setup_blends[index], regs.blend_per_target[index]); } - blend_setup(regs.blend_per_target[index]); } + scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) { cmdbuf.SetColorBlendEquationEXT(0, setup_blends); });