From 11ba623f267814885c2d69c0f1214c9c576eb916 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 14 Jan 2020 10:57:35 +1000 Subject: [PATCH] Add an option to present/skip presenting duplicate frames Currently, we do not display every second frame in 25fps/30fps games which run to vsync. This improves performance as there's less rendering for the GPU to perform, but when combined with vsync, could cause frame pacing issues. This commit adds an option to force every frame generated by the console to be displayed to the host, which may improve pacing for these games. --- .../ui/SettingsFragmentPresenter.java | 4 ++ .../features/settings/utils/SettingsFile.java | 1 + .../app/src/main/res/values/strings.xml | 2 + Source/Core/Core/Config/GraphicsSettings.cpp | 2 + Source/Core/Core/Config/GraphicsSettings.h | 1 + .../Core/ConfigLoaders/IsSettingSaveable.cpp | 3 +- .../DolphinQt/Config/Graphics/HacksWidget.cpp | 20 ++++++++++ .../DolphinQt/Config/Graphics/HacksWidget.h | 2 + Source/Core/VideoCommon/RenderBase.cpp | 40 +++++++++++-------- Source/Core/VideoCommon/VideoConfig.cpp | 1 + Source/Core/VideoCommon/VideoConfig.h | 1 + 11 files changed, 60 insertions(+), 17 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 3c3f2f294f..9bf66e3cbe 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -585,6 +585,7 @@ public final class SettingsFragmentPresenter Setting gpuTextureDecoding = gfxSection.getSetting(SettingsFile.KEY_GPU_TEXTURE_DECODING); Setting xfbToTexture = hacksSection.getSetting(SettingsFile.KEY_XFB_TEXTURE); Setting immediateXfb = hacksSection.getSetting(SettingsFile.KEY_IMMEDIATE_XFB); + Setting skipDuplicateXfbs = hacksSection.getSetting(SettingsFile.KEY_SKIP_DUPLICATE_XFBS); Setting fastDepth = gfxSection.getSetting(SettingsFile.KEY_FAST_DEPTH); sl.add(new HeaderSetting(null, null, R.string.embedded_frame_buffer, 0)); @@ -613,6 +614,9 @@ public final class SettingsFragmentPresenter R.string.xfb_copy_method, R.string.xfb_copy_method_description, true, xfbToTexture)); sl.add(new CheckBoxSetting(SettingsFile.KEY_IMMEDIATE_XFB, Settings.SECTION_GFX_HACKS, R.string.immediate_xfb, R.string.immediate_xfb_description, false, immediateXfb)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_SKIP_DUPLICATE_XFBS, Settings.SECTION_GFX_HACKS, + R.string.skip_duplicate_xfbs, R.string.skip_duplicate_xfbs_description, true, + skipDuplicateXfbs)); sl.add(new HeaderSetting(null, null, R.string.other, 0)); sl.add(new CheckBoxSetting(SettingsFile.KEY_FAST_DEPTH, Settings.SECTION_GFX_SETTINGS, diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java index 8de9bf5281..c4e61b8646 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java @@ -89,6 +89,7 @@ public final class SettingsFile public static final String KEY_GPU_TEXTURE_DECODING = "EnableGPUTextureDecoding"; public static final String KEY_XFB_TEXTURE = "XFBToTextureEnable"; public static final String KEY_IMMEDIATE_XFB = "ImmediateXFBEnable"; + public static final String KEY_SKIP_DUPLICATE_XFBS = "SkipDuplicateXFBs"; public static final String KEY_FAST_DEPTH = "FastDepthCalc"; public static final String KEY_ASPECT_RATIO = "AspectRatio"; public static final String KEY_SHADER_COMPILATION_MODE = "ShaderCompilationMode"; diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 1e7ef2114a..7be10374cf 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -232,6 +232,8 @@ Stores XFB Copies exclusively on the GPU, bypassing system memory. Causes graphical defects in a small number of games that need to readback from memory. If unsure, leave this checked. Immediately Present XFB Displays the XFB copies as soon as they are created, without waiting for scanout. Causes graphical defects in some games but reduces latency. If unsure, leave this unchecked. + Immediately Present XFB + Skips presentation of duplicate frames. This may improve performance on low-end devices, while making frame pacing less consistent. If unsure, leave this checked. Disable Destination Alpha Disables emulation of a hardware feature called destination alpha, which is used in many games for various effects. Fast Depth Calculation diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index 7068943cc2..af9647aa29 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -154,6 +154,8 @@ const ConfigInfo GFX_HACK_DISABLE_COPY_TO_VRAM{{System::GFX, "Hacks", "Dis false}; const ConfigInfo GFX_HACK_DEFER_EFB_COPIES{{System::GFX, "Hacks", "DeferEFBCopies"}, true}; const ConfigInfo GFX_HACK_IMMEDIATE_XFB{{System::GFX, "Hacks", "ImmediateXFBEnable"}, false}; +const ConfigInfo GFX_HACK_SKIP_DUPLICATE_XFBS{{System::GFX, "Hacks", "SkipDuplicateXFBs"}, + true}; const ConfigInfo GFX_HACK_COPY_EFB_SCALED{{System::GFX, "Hacks", "EFBScaledCopy"}, true}; const ConfigInfo GFX_HACK_EFB_EMULATE_FORMAT_CHANGES{ {System::GFX, "Hacks", "EFBEmulateFormatChanges"}, false}; diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index dc8d501fab..cceef8f935 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -111,6 +111,7 @@ extern const ConfigInfo GFX_HACK_SKIP_XFB_COPY_TO_RAM; extern const ConfigInfo GFX_HACK_DISABLE_COPY_TO_VRAM; extern const ConfigInfo GFX_HACK_DEFER_EFB_COPIES; extern const ConfigInfo GFX_HACK_IMMEDIATE_XFB; +extern const ConfigInfo GFX_HACK_SKIP_DUPLICATE_XFBS; extern const ConfigInfo GFX_HACK_COPY_EFB_SCALED; extern const ConfigInfo GFX_HACK_EFB_EMULATE_FORMAT_CHANGES; extern const ConfigInfo GFX_HACK_VERTEX_ROUDING; diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index 5f17e6cac1..f58aec2cd4 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -28,7 +28,7 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location) return true; } - static constexpr std::array s_setting_saveable = { + static constexpr std::array s_setting_saveable = { // Main.Core &Config::MAIN_DEFAULT_ISO.location, @@ -136,6 +136,7 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location) &Config::GFX_HACK_DISABLE_COPY_TO_VRAM.location, &Config::GFX_HACK_DEFER_EFB_COPIES.location, &Config::GFX_HACK_IMMEDIATE_XFB.location, + &Config::GFX_HACK_SKIP_DUPLICATE_XFBS.location, &Config::GFX_HACK_COPY_EFB_SCALED.location, &Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES.location, &Config::GFX_HACK_VERTEX_ROUDING.location, diff --git a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp index f5db93fb14..2229a51ddf 100644 --- a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp @@ -86,9 +86,12 @@ void HacksWidget::CreateWidgets() m_store_xfb_copies = new GraphicsBool(tr("Store XFB Copies to Texture Only"), Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM); m_immediate_xfb = new GraphicsBool(tr("Immediately Present XFB"), Config::GFX_HACK_IMMEDIATE_XFB); + m_skip_duplicate_xfbs = new GraphicsBool(tr("Skip Presenting Duplicate Frames"), + Config::GFX_HACK_SKIP_DUPLICATE_XFBS); xfb_layout->addWidget(m_store_xfb_copies); xfb_layout->addWidget(m_immediate_xfb); + xfb_layout->addWidget(m_skip_duplicate_xfbs); // Other auto* other_box = new QGroupBox(tr("Other")); @@ -117,6 +120,7 @@ void HacksWidget::CreateWidgets() setLayout(main_layout); UpdateDeferEFBCopiesEnabled(); + UpdateSkipPresentingDuplicateFramesEnabled(); } void HacksWidget::OnBackendChanged(const QString& backend_name) @@ -140,6 +144,8 @@ void HacksWidget::ConnectWidgets() [this](int) { UpdateDeferEFBCopiesEnabled(); }); connect(m_store_xfb_copies, &QCheckBox::stateChanged, [this](int) { UpdateDeferEFBCopiesEnabled(); }); + connect(m_immediate_xfb, &QCheckBox::stateChanged, + [this](int) { UpdateSkipPresentingDuplicateFramesEnabled(); }); } void HacksWidget::LoadSettings() @@ -235,6 +241,12 @@ void HacksWidget::AddDescriptions() "expect all XFB copies to be displayed. However, turning this setting on reduces " "latency.\n\nIf unsure, leave this unchecked."); + static const char TR_SKIP_DUPLICATE_XFBS_DESCRIPTION[] = QT_TR_NOOP( + "Skips presentation of duplicate frames (XFB copies) in 25fps/30fps games. This may improve " + "performance on low-end devices, while making frame pacing less consistent.\n\nDisable this " + "option as well as enabling V-Sync for optimal frame pacing.\n\nIf unsure, leave this " + "checked."); + static const char TR_GPU_DECODING_DESCRIPTION[] = QT_TR_NOOP("Enables texture decoding using the GPU instead of the CPU.\n\nThis may result in " "performance gains in some scenarios, or on systems where the CPU is the " @@ -263,6 +275,7 @@ void HacksWidget::AddDescriptions() AddDescription(m_accuracy, TR_ACCUARCY_DESCRIPTION); AddDescription(m_store_xfb_copies, TR_STORE_XFB_TO_TEXTURE_DESCRIPTION); AddDescription(m_immediate_xfb, TR_IMMEDIATE_XFB_DESCRIPTION); + AddDescription(m_skip_duplicate_xfbs, TR_SKIP_DUPLICATE_XFBS_DESCRIPTION); AddDescription(m_gpu_texture_decoding, TR_GPU_DECODING_DESCRIPTION); AddDescription(m_fast_depth_calculation, TR_FAST_DEPTH_CALC_DESCRIPTION); AddDescription(m_disable_bounding_box, TR_DISABLE_BOUNDINGBOX_DESCRIPTION); @@ -277,3 +290,10 @@ void HacksWidget::UpdateDeferEFBCopiesEnabled() const bool can_defer = m_store_efb_copies->isChecked() && m_store_xfb_copies->isChecked(); m_defer_efb_copies->setEnabled(!can_defer); } + +void HacksWidget::UpdateSkipPresentingDuplicateFramesEnabled() +{ + // If Immediate XFB is on, there's no point to skipping duplicate XFB copies as immediate presents + // when the XFB is created, therefore all XFB copies will be unique. + m_skip_duplicate_xfbs->setEnabled(!m_immediate_xfb->isChecked()); +} diff --git a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h index d46cb67932..b6ca90dbd4 100644 --- a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h @@ -37,6 +37,7 @@ private: // External Framebuffer QCheckBox* m_store_xfb_copies; QCheckBox* m_immediate_xfb; + QCheckBox* m_skip_duplicate_xfbs; // Other QCheckBox* m_fast_depth_calculation; @@ -50,4 +51,5 @@ private: void AddDescriptions(); void UpdateDeferEFBCopiesEnabled(); + void UpdateSkipPresentingDuplicateFramesEnabled(); }; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index d94eac2c0d..71281defdf 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -1190,8 +1190,10 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 MathUtil::Rectangle xfb_rect; const auto* xfb_entry = g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &xfb_rect); - if (xfb_entry && xfb_entry->id != m_last_xfb_id) + if (xfb_entry && + (!g_ActiveConfig.bSkipPresentingDuplicateXFBs || xfb_entry->id != m_last_xfb_id)) { + const bool is_duplicate_frame = xfb_entry->id == m_last_xfb_id; m_last_xfb_id = xfb_entry->id; // Since we use the common pipelines here and draw vertices if a batch is currently being @@ -1235,20 +1237,24 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 SetWindowSize(xfb_rect.GetWidth(), xfb_rect.GetHeight()); } - m_fps_counter.Update(); + if (!is_duplicate_frame) + { + m_fps_counter.Update(); - DolphinAnalytics::PerformanceSample perf_sample; - perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance(); - perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims; - perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls; - DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample)); + DolphinAnalytics::PerformanceSample perf_sample; + perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance(); + perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims; + perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls; + DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample)); - if (IsFrameDumping()) - DumpCurrentFrame(xfb_entry->texture.get(), xfb_rect, ticks); + if (IsFrameDumping()) + DumpCurrentFrame(xfb_entry->texture.get(), xfb_rect, ticks); + + // Begin new frame + m_frame_count++; + g_stats.ResetFrame(); + } - // Begin new frame - m_frame_count++; - g_stats.ResetFrame(); g_shader_cache->RetrieveAsyncShaders(); g_vertex_manager->OnEndFrame(); BeginImGuiFrame(); @@ -1263,16 +1269,18 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 // rate and not waiting for vblank. Otherwise, we'd end up with a huge list of pending copies. g_texture_cache->FlushEFBCopies(); - // Remove stale EFB/XFB copies. - g_texture_cache->Cleanup(m_frame_count); + if (!is_duplicate_frame) + { + // Remove stale EFB/XFB copies. + g_texture_cache->Cleanup(m_frame_count); + Core::Callback_VideoCopiedToXFB(true); + } // Handle any config changes, this gets propogated to the backend. CheckForConfigChanges(); g_Config.iSaveTargetId = 0; EndUtilityDrawing(); - - Core::Callback_VideoCopiedToXFB(true); } else { diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index bfd4a6b99b..b624755b11 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -152,6 +152,7 @@ void VideoConfig::Refresh() bDisableCopyToVRAM = Config::Get(Config::GFX_HACK_DISABLE_COPY_TO_VRAM); bDeferEFBCopies = Config::Get(Config::GFX_HACK_DEFER_EFB_COPIES); bImmediateXFB = Config::Get(Config::GFX_HACK_IMMEDIATE_XFB); + bSkipPresentingDuplicateXFBs = Config::Get(Config::GFX_HACK_SKIP_DUPLICATE_XFBS); bCopyEFBScaled = Config::Get(Config::GFX_HACK_COPY_EFB_SCALED); bEFBEmulateFormatChanges = Config::Get(Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES); bVertexRounding = Config::Get(Config::GFX_HACK_VERTEX_ROUDING); diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index 0a31b3c943..3f18f458a0 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -124,6 +124,7 @@ struct VideoConfig final bool bDisableCopyToVRAM; bool bDeferEFBCopies; bool bImmediateXFB; + bool bSkipPresentingDuplicateXFBs; bool bCopyEFBScaled; int iSafeTextureCache_ColorSamples; float fAspectRatioHackW, fAspectRatioHackH;