From e9bfca8ccac43899866a12e19523944e1cd1864a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 15 Feb 2025 12:38:30 +1000 Subject: [PATCH] System: Add 'Fast Forward Memory Card Access' option Does what it says on the tin. --- src/core/fullscreen_ui.cpp | 6 ++++++ src/core/memory_card.cpp | 6 ++++-- src/core/settings.cpp | 2 ++ src/core/settings.h | 1 + src/core/system.cpp | 21 +++++++++++++++++++- src/core/system_private.h | 3 +++ src/duckstation-qt/consolesettingswidget.cpp | 5 +++++ src/duckstation-qt/consolesettingswidget.ui | 11 ++++++++-- 8 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index 9f31ba86d..58b5ebeb2 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -4016,6 +4016,10 @@ void FullscreenUI::DrawConsoleSettingsPage() "may vary between games."), "BIOS", "FastForwardBoot", false, GetEffectiveBoolSetting(bsi, "BIOS", "PatchFastBoot", Settings::DEFAULT_FAST_BOOT_VALUE)); + DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_SD_CARD, "Fast Forward Memory Card Access"), + FSUI_CSTR("Fast forwards through memory card access, both loading and saving. Can reduce waiting " + "times in games that frequently access memory cards."), + "MemoryCards", "FastForwardAccess", false); DrawToggleSetting( bsi, FSUI_ICONSTR(ICON_FA_MEMORY, "Enable 8MB RAM"), FSUI_CSTR("Enables an additional 6MB of RAM to obtain a total of 2+6 = 8MB, usually present on dev consoles."), @@ -8980,8 +8984,10 @@ TRANSLATE_NOOP("FullscreenUI", "Failed to load shader {}. It may be invalid.\nEr TRANSLATE_NOOP("FullscreenUI", "Failed to save controller preset '{}'."); TRANSLATE_NOOP("FullscreenUI", "Fast Boot"); TRANSLATE_NOOP("FullscreenUI", "Fast Forward Boot"); +TRANSLATE_NOOP("FullscreenUI", "Fast Forward Memory Card Access"); TRANSLATE_NOOP("FullscreenUI", "Fast Forward Speed"); TRANSLATE_NOOP("FullscreenUI", "Fast Forward Volume"); +TRANSLATE_NOOP("FullscreenUI", "Fast forwards through memory card access, both loading and saving. Can reduce waiting times in games that frequently access memory cards."); TRANSLATE_NOOP("FullscreenUI", "Fast forwards through the early loading process when fast booting, saving time. Results may vary between games."); TRANSLATE_NOOP("FullscreenUI", "File Size"); TRANSLATE_NOOP("FullscreenUI", "File Size: %u MB (%u MB on disk)"); diff --git a/src/core/memory_card.cpp b/src/core/memory_card.cpp index 314472d79..e58e542b6 100644 --- a/src/core/memory_card.cpp +++ b/src/core/memory_card.cpp @@ -1,9 +1,9 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #include "memory_card.h" #include "host.h" -#include "system.h" +#include "system_private.h" #include "util/imgui_manager.h" #include "util/state_wrapper.h" @@ -143,6 +143,7 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out) { DEV_LOG("Reading memory card sector {}", m_address); m_checksum = Truncate8(m_address >> 8) ^ Truncate8(m_address) ^ bits; + System::OnMemoryCardAccessed(); } else { @@ -178,6 +179,7 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out) INFO_LOG("Writing memory card sector {}", m_address); m_checksum = Truncate8(m_address >> 8) ^ Truncate8(m_address) ^ data_in; m_FLAG.no_write_yet = false; + System::OnMemoryCardAccessed(); } else { diff --git a/src/core/settings.cpp b/src/core/settings.cpp index d9b9acf15..3f981c438 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -420,6 +420,7 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro memory_card_paths[0] = si.GetStringValue("MemoryCards", "Card1Path", ""); memory_card_paths[1] = si.GetStringValue("MemoryCards", "Card2Path", ""); memory_card_use_playlist_title = si.GetBoolValue("MemoryCards", "UsePlaylistTitle", true); + memory_card_fast_forward_access = si.GetBoolValue("MemoryCards", "FastForwardAccess", false); achievements_enabled = si.GetBoolValue("Cheevos", "Enabled", false); achievements_hardcore_mode = si.GetBoolValue("Cheevos", "ChallengeMode", false); @@ -698,6 +699,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const si.DeleteValue("MemoryCards", "Card2Path"); si.SetBoolValue("MemoryCards", "UsePlaylistTitle", memory_card_use_playlist_title); + si.SetBoolValue("MemoryCards", "FastForwardAccess", memory_card_fast_forward_access); si.SetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(multitap_mode)); diff --git a/src/core/settings.h b/src/core/settings.h index 0e9cc4899..42d8d6b8e 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -322,6 +322,7 @@ struct Settings : public GPUSettings bool bios_fast_forward_boot : 1 = false; bool enable_8mb_ram : 1 = false; bool memory_card_use_playlist_title : 1 = true; + bool memory_card_fast_forward_access : 1 = false; bool pio_switch_active : 1 = true; bool pio_flash_write_enable : 1 = false; bool pcdrv_enable_writes : 1 = false; diff --git a/src/core/system.cpp b/src/core/system.cpp index 7f55a3b0a..b41a503a0 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -121,6 +121,7 @@ static constexpr float PRE_FRAME_SLEEP_UPDATE_INTERVAL = 1.0f; static constexpr const char FALLBACK_EXE_NAME[] = "PSX.EXE"; static constexpr u32 MAX_SKIPPED_DUPLICATE_FRAME_COUNT = 2; // 20fps minimum static constexpr u32 MAX_SKIPPED_TIMEOUT_FRAME_COUNT = 1; // 30fps minimum +static constexpr u8 MEMORY_CARD_FAST_FORWARD_FRAMES = 30; namespace { @@ -263,6 +264,7 @@ struct ALIGN_TO_CACHE_LINE StateVars bool turbo_enabled = false; bool runahead_replay_pending = false; + u8 memory_card_fast_forward_frames = 0; u32 skipped_frame_count = 0; u32 last_presented_internal_frame_number = 0; @@ -2140,6 +2142,14 @@ void System::FrameDone() Timer::Value current_time = Timer::GetCurrentValue(); GTE::UpdateFreecam(current_time); + // memory card fast forward + if (s_state.memory_card_fast_forward_frames > 0) + { + s_state.memory_card_fast_forward_frames--; + if (s_state.memory_card_fast_forward_frames == 0) + UpdateSpeedLimiterState(); + } + // Frame step after runahead, otherwise the pause takes precedence and the replay never happens. if (s_state.frame_step_request) { @@ -3523,7 +3533,7 @@ void System::UpdateSpeedLimiterState() { DebugAssert(IsValid()); - s_state.target_speed = IsFastForwardingBoot() ? + s_state.target_speed = (IsFastForwardingBoot() || s_state.memory_card_fast_forward_frames > 0) ? 0.0f : (s_state.turbo_enabled ? g_settings.turbo_speed : (s_state.fast_forward_enabled ? g_settings.fast_forward_speed : @@ -3931,6 +3941,15 @@ bool System::IsSavingMemoryCards() return false; } +void System::OnMemoryCardAccessed() +{ + if (!g_settings.memory_card_fast_forward_access) + return; + + if (std::exchange(s_state.memory_card_fast_forward_frames, MEMORY_CARD_FAST_FORWARD_FRAMES) == 0) + UpdateSpeedLimiterState(); +} + void System::SwapMemoryCards() { if (!IsValid()) diff --git a/src/core/system_private.h b/src/core/system_private.h index 54b089fa2..ee920a92a 100644 --- a/src/core/system_private.h +++ b/src/core/system_private.h @@ -50,6 +50,9 @@ void DisplayWindowResized(); /// Updates the internal GTE aspect ratio. Use with "match display" aspect ratio setting. void UpdateGTEAspectRatio(); +/// Called on card read/write, handles fast forwarding. +void OnMemoryCardAccessed(); + /// Immediately terminates the virtual machine, no state is saved. void AbnormalShutdown(const std::string_view reason); diff --git a/src/duckstation-qt/consolesettingswidget.cpp b/src/duckstation-qt/consolesettingswidget.cpp index b4c199224..829eddd8a 100644 --- a/src/duckstation-qt/consolesettingswidget.cpp +++ b/src/duckstation-qt/consolesettingswidget.cpp @@ -51,6 +51,8 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* pa SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "BIOS", "PatchFastBoot", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastForwardBoot, "BIOS", "FastForwardBoot", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable8MBRAM, "Console", "Enable8MBRAM", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastForwardMemoryCardAccess, "MemoryCards", + +"FastForwardAccess", false); connect(m_ui.fastBoot, &QCheckBox::checkStateChanged, this, &ConsoleSettingsWidget::onFastBootChanged); onFastBootChanged(); @@ -92,6 +94,9 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* pa "to use a larger heap size for " "this additional RAM to be usable. Titles which rely on memory mirrors may break, so it should only be used " "with compatible mods.")); + m_dialog->registerWidgetHelp(m_ui.fastForwardMemoryCardAccess, tr("Fast Forward Memory Card Access"), tr("Unchecked"), + tr("Fast forwards through memory card access, both loading and saving. Can reduce " + "waiting times in games that frequently access memory cards.")); dialog->registerWidgetHelp(m_ui.cpuExecutionMode, tr("Execution Mode"), tr("Recompiler (Fastest)"), tr("Determines how the emulated CPU executes instructions.")); diff --git a/src/duckstation-qt/consolesettingswidget.ui b/src/duckstation-qt/consolesettingswidget.ui index eba0d6d12..4bc30da10 100644 --- a/src/duckstation-qt/consolesettingswidget.ui +++ b/src/duckstation-qt/consolesettingswidget.ui @@ -48,14 +48,21 @@ - + Fast Forward Boot - + + + + Fast Forward Memory Card Access + + + + Enable 8MB RAM (Dev Console)