diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java index a02e8f0c7e..96383c1a87 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java @@ -247,6 +247,7 @@ public enum BooleanSetting implements AbstractBooleanSetting GFX_HACK_EFB_EMULATE_FORMAT_CHANGES(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "EFBEmulateFormatChanges", false), GFX_HACK_VERTEX_ROUNDING(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "VertexRounding", false), + GFX_HACK_VI_SKIP(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "VISkip", false), GFX_HACK_FAST_TEXTURE_SAMPLING(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "FastTextureSampling", true), 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 76d5d8988d..8818ae57ba 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 @@ -881,6 +881,8 @@ public final class SettingsFragmentPresenter R.string.disable_bbox, R.string.disable_bbox_description)); sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_VERTEX_ROUNDING, R.string.vertex_rounding, R.string.vertex_rounding_description)); + sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_VI_SKIP, R.string.vi_skip, + R.string.vi_skip_description)); sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SAVE_TEXTURE_CACHE_TO_STATE, R.string.texture_cache_to_state, R.string.texture_cache_to_state_description)); } diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 7d99d030f0..d2aa1213d6 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -309,6 +309,8 @@ Disables bounding box emulation. This may improve GPU performance significantly, but some games will break. If unsure, leave this checked. Vertex Rounding Rounds 2D vertices to whole pixels and rounds the viewport size to a whole number. Fixes graphical problems in some games at higher internal resolutions. This setting has no effect when native internal resolution is used. If unsure, leave this unchecked. + VI Skip + Skips VI interrupts when lag is detected, allowing for smooth audio playback when emulation speed is not 100%. Can cause freezes and compatibility issues. Save Texture Cache to State Includes the contents of the embedded frame buffer (EFB) and upscaled EFB copies in save states. Fixes missing and/or non-upscaled textures/objects when loading states at the cost of additional save/load time. Aspect Ratio diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index c52403d476..816179a565 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -156,6 +156,7 @@ const Info GFX_HACK_COPY_EFB_SCALED{{System::GFX, "Hacks", "EFBScaledCopy" const Info GFX_HACK_EFB_EMULATE_FORMAT_CHANGES{ {System::GFX, "Hacks", "EFBEmulateFormatChanges"}, false}; const Info GFX_HACK_VERTEX_ROUNDING{{System::GFX, "Hacks", "VertexRounding"}, false}; +const Info GFX_HACK_VI_SKIP{{System::GFX, "Hacks", "VISkip"}, false}; const Info GFX_HACK_MISSING_COLOR_VALUE{{System::GFX, "Hacks", "MissingColorValue"}, 0xFFFFFFFF}; const Info GFX_HACK_FAST_TEXTURE_SAMPLING{{System::GFX, "Hacks", "FastTextureSampling"}, diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index df97bd12c2..f53248995d 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -135,6 +135,7 @@ extern const Info GFX_HACK_EARLY_XFB_OUTPUT; extern const Info GFX_HACK_COPY_EFB_SCALED; extern const Info GFX_HACK_EFB_EMULATE_FORMAT_CHANGES; extern const Info GFX_HACK_VERTEX_ROUNDING; +extern const Info GFX_HACK_VI_SKIP; extern const Info GFX_HACK_MISSING_COLOR_VALUE; extern const Info GFX_HACK_FAST_TEXTURE_SAMPLING; #ifdef __APPLE__ diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index a6f3a890aa..c3f1456f60 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -39,6 +39,7 @@ const Info MAIN_JIT_FOLLOW_BRANCH{{System::Main, "Core", "JITFollowBranch" const Info MAIN_FASTMEM{{System::Main, "Core", "Fastmem"}, true}; const Info MAIN_ACCURATE_CPU_CACHE{{System::Main, "Core", "AccurateCPUCache"}, false}; const Info MAIN_DSP_HLE{{System::Main, "Core", "DSPHLE"}, true}; +const Info MAIN_MAX_FALLBACK{{System::Main, "Core", "MaxFallback"}, 100}; const Info MAIN_TIMING_VARIANCE{{System::Main, "Core", "TimingVariance"}, 40}; const Info MAIN_CPU_THREAD{{System::Main, "Core", "CPUThread"}, true}; const Info MAIN_SYNC_ON_SKIP_IDLE{{System::Main, "Core", "SyncOnSkipIdle"}, true}; diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 92b909adf5..d6a8d058f5 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -58,6 +58,7 @@ extern const Info MAIN_FASTMEM; extern const Info MAIN_ACCURATE_CPU_CACHE; // Should really be in the DSP section, but we're kind of stuck with bad decisions made in the past. extern const Info MAIN_DSP_HLE; +extern const Info MAIN_MAX_FALLBACK; extern const Info MAIN_TIMING_VARIANCE; extern const Info MAIN_CPU_THREAD; extern const Info MAIN_SYNC_ON_SKIP_IDLE; diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index fedf6c6078..86c0ffc5fb 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -111,6 +111,7 @@ bool IsSettingSaveable(const Config::Location& config_location) &Config::MAIN_SYNC_ON_SKIP_IDLE.GetLocation(), &Config::MAIN_FASTMEM.GetLocation(), &Config::MAIN_TIMING_VARIANCE.GetLocation(), + &Config::MAIN_MAX_FALLBACK.GetLocation(), &Config::MAIN_WII_SD_CARD.GetLocation(), &Config::MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC.GetLocation(), &Config::MAIN_WII_KEYBOARD.GetLocation(), diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp index eb7b537a61..11979c103d 100644 --- a/Source/Core/Core/CoreTiming.cpp +++ b/Source/Core/Core/CoreTiming.cpp @@ -24,6 +24,7 @@ #include "VideoCommon/Fifo.h" #include "VideoCommon/PerformanceMetrics.h" #include "VideoCommon/VideoBackendBase.h" +#include "VideoCommon/VideoConfig.h" namespace CoreTiming { @@ -354,7 +355,7 @@ void CoreTimingManager::Throttle(const s64 target_cycle) // A maximum fallback is used to prevent the system from sleeping for // too long or going full speed in an attempt to catch up to timings. const DT max_fallback = - std::chrono::duration_cast
(DT_ms(Config::Get(Config::MAIN_TIMING_VARIANCE))); + std::chrono::duration_cast
(DT_ms(Config::Get(Config::MAIN_MAX_FALLBACK))); const TimePoint time = Clock::now(); const TimePoint min_deadline = time - max_fallback; @@ -371,6 +372,13 @@ void CoreTimingManager::Throttle(const s64 target_cycle) m_throttle_deadline = min_deadline; } + // Skip the VI interrupt if the CPU is lagging by a certain amount. + // It doesn't matter what amount of lag we skip VI at, as long as it's constant. + const DT max_variance = + std::chrono::duration_cast
(DT_ms(Config::Get(Config::MAIN_TIMING_VARIANCE))); + const TimePoint vi_deadline = time - max_variance; + m_throttle_disable_vi_int = 0.0 < speed && m_throttle_deadline < vi_deadline; + // Only sleep if we are behind the deadline if (time < m_throttle_deadline) { @@ -388,6 +396,11 @@ TimePoint CoreTimingManager::GetCPUTimePoint(s64 cyclesLate) const m_throttle_clock_per_sec)); } +bool CoreTimingManager::GetVISkip() const +{ + return m_throttle_disable_vi_int && g_ActiveConfig.bVISkip && !Core::WantsDeterminism(); +} + void CoreTimingManager::LogPendingEvents() const { auto clone = m_event_queue; diff --git a/Source/Core/Core/CoreTiming.h b/Source/Core/Core/CoreTiming.h index 1a860c5fa5..7e72ce9483 100644 --- a/Source/Core/Core/CoreTiming.h +++ b/Source/Core/Core/CoreTiming.h @@ -146,6 +146,7 @@ public: void Throttle(const s64 target_cycle); TimePoint GetCPUTimePoint(s64 cyclesLate) const; // Used by Dolphin Analytics + bool GetVISkip() const; // Used By VideoInterface private: Globals m_globals; @@ -184,6 +185,7 @@ private: TimePoint m_throttle_deadline = Clock::now(); s64 m_throttle_clock_per_sec; s64 m_throttle_min_clock_per_sleep; + bool m_throttle_disable_vi_int = false; int DowncountToCycles(int downcount) const; int CyclesToDowncount(int cycles) const; diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp index f40f475c4d..cb5b6bcfa2 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -952,6 +952,10 @@ void Update(u64 ticks) state.ticks_last_line_start = system.GetCoreTiming().GetTicks(); } + // TODO: Findout why skipping interrupts acts as a frameskip + if (system.GetCoreTiming().GetVISkip()) + return; + // Check if we need to assert IR_INT. Note that the granularity of our current horizontal // position is limited to half-lines. diff --git a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp index 0519631b77..484a1089b5 100644 --- a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp @@ -106,11 +106,13 @@ void HacksWidget::CreateWidgets() m_vertex_rounding = new GraphicsBool(tr("Vertex Rounding"), Config::GFX_HACK_VERTEX_ROUNDING); m_save_texture_cache_state = new GraphicsBool(tr("Save Texture Cache to State"), Config::GFX_SAVE_TEXTURE_CACHE_TO_STATE); + m_vi_skip = new GraphicsBool(tr("VI Skip"), Config::GFX_HACK_VI_SKIP); other_layout->addWidget(m_fast_depth_calculation, 0, 0); other_layout->addWidget(m_disable_bounding_box, 0, 1); other_layout->addWidget(m_vertex_rounding, 1, 0); other_layout->addWidget(m_save_texture_cache_state, 1, 1); + other_layout->addWidget(m_vi_skip, 2, 0); main_layout->addWidget(efb_box); main_layout->addWidget(texture_cache_box); @@ -148,6 +150,8 @@ void HacksWidget::ConnectWidgets() [this](int) { UpdateDeferEFBCopiesEnabled(); }); connect(m_immediate_xfb, &QCheckBox::stateChanged, [this](int) { UpdateSkipPresentingDuplicateFramesEnabled(); }); + connect(m_vi_skip, &QCheckBox::stateChanged, + [this](int) { UpdateSkipPresentingDuplicateFramesEnabled(); }); } void HacksWidget::LoadSettings() @@ -280,6 +284,12 @@ void HacksWidget::AddDescriptions() "Fixes graphical problems in some games at higher internal resolutions. This setting has no " "effect when native internal resolution is used.

" "If unsure, leave this unchecked."); + static const char TR_VI_SKIP_DESCRIPTION[] = + QT_TR_NOOP("Skips VI interrupts when lag is detected, allowing for " + "smooth audio playback when emulation speed is not 100%.

" + "WARNING: Can cause freezes and compatibility " + "issues.

" + "If unsure, leave this unchecked."); m_skip_efb_cpu->SetDescription(tr(TR_SKIP_EFB_CPU_ACCESS_DESCRIPTION)); m_ignore_format_changes->SetDescription(tr(TR_IGNORE_FORMAT_CHANGE_DESCRIPTION)); @@ -295,6 +305,7 @@ void HacksWidget::AddDescriptions() m_disable_bounding_box->SetDescription(tr(TR_DISABLE_BOUNDINGBOX_DESCRIPTION)); m_save_texture_cache_state->SetDescription(tr(TR_SAVE_TEXTURE_CACHE_TO_STATE_DESCRIPTION)); m_vertex_rounding->SetDescription(tr(TR_VERTEX_ROUNDING_DESCRIPTION)); + m_vi_skip->SetDescription(tr(TR_VI_SKIP_DESCRIPTION)); } void HacksWidget::UpdateDeferEFBCopiesEnabled() @@ -309,5 +320,12 @@ 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()); + // This setting is also required for VI skip to work. + + const bool disabled = m_immediate_xfb->isChecked() || m_vi_skip->isChecked(); + + if (disabled) + m_skip_duplicate_xfbs->setChecked(true); + + m_skip_duplicate_xfbs->setEnabled(!disabled); } diff --git a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h index 490a5b4f7e..296a467444 100644 --- a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h @@ -42,6 +42,7 @@ private: GraphicsBool* m_fast_depth_calculation; GraphicsBool* m_disable_bounding_box; GraphicsBool* m_vertex_rounding; + GraphicsBool* m_vi_skip; GraphicsBool* m_save_texture_cache_state; void CreateWidgets(); diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 66ba09c581..5314e352ad 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -140,7 +140,8 @@ 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); + bVISkip = Config::Get(Config::GFX_HACK_VI_SKIP); + bSkipPresentingDuplicateXFBs = bVISkip || 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_ROUNDING); diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index 34a0063013..8140eb27eb 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -153,6 +153,7 @@ struct VideoConfig final bool bEnablePixelLighting = false; bool bFastDepthCalc = false; bool bVertexRounding = false; + bool bVISkip = false; int iEFBAccessTileSize = 0; int iSaveTargetId = 0; // TODO: Should be dropped u32 iMissingColorValue = 0;