diff --git a/Core/Config.cpp b/Core/Config.cpp index c2162ddece..73ed515c97 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -75,6 +75,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { general->Get("EnableCheats", &bEnableCheats, false); general->Get("ScreenshotsAsPNG", &bScreenshotsAsPNG, false); general->Get("StateSlot", &iCurrentStateSlot, 0); + general->Get("RewindFlipFrequency", &iRewindFlipFrequency, 0); general->Get("GridView1", &bGridView1, true); general->Get("GridView2", &bGridView2, true); general->Get("GridView3", &bGridView3, true); @@ -341,6 +342,7 @@ void Config::Save() { general->Set("EnableCheats", bEnableCheats); general->Set("ScreenshotsAsPNG", bScreenshotsAsPNG); general->Set("StateSlot", iCurrentStateSlot); + general->Set("RewindFlipFrequency", iRewindFlipFrequency); general->Set("GridView1", bGridView1); general->Set("GridView2", bGridView2); general->Set("GridView3", bGridView3); diff --git a/Core/Config.h b/Core/Config.h index 7358ee7076..d28168d366 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -92,6 +92,7 @@ public: int iForceMaxEmulatedFPS; int iMaxRecent; int iCurrentStateSlot; + int iRewindFlipFrequency; bool bEnableCheats; bool bReloadCheats; bool bDisableStencilTest; diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index d0278a7f57..b475dcbf55 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -520,7 +520,7 @@ public: virtual void DoState(PointerWrap &p) { - auto s = p.Section("Thread", 1); + auto s = p.Section("Thread", 1, 2); if (!s) return; @@ -537,6 +537,12 @@ public: p.Do(pendingMipsCalls); p.Do(pushedStacks); p.Do(currentStack); + + if (s >= 2) + { + p.Do(waitingThreads); + p.Do(pausedWaits); + } } NativeThread nt; diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 0f71933c16..4cb48e7d65 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -75,7 +75,7 @@ namespace SaveState CChunkFileReader::Error Save() { int n = next_++ % size_; - if (n == first_) + if ((next_ % size_) == first_) ++first_; SaveStart state; @@ -89,7 +89,7 @@ namespace SaveState CChunkFileReader::Error Restore() { // No valid states left. - if (next_ == first_) + if (Empty()) return CChunkFileReader::ERROR_BAD_FILE; int n = (next_-- + size_) % size_; @@ -100,6 +100,17 @@ namespace SaveState return CChunkFileReader::LoadPtr(&states_[n][0], state); } + void Clear() + { + first_ = 0; + next_ = 0; + } + + bool Empty() + { + return next_ == first_; + } + typedef std::vector StateBuffer; int first_; int next_; @@ -114,8 +125,6 @@ namespace SaveState // TODO: Should this be configurable? static const int REWIND_NUM_STATES = 5; static StateRingbuffer rewindStates(REWIND_NUM_STATES); - // TODO: g_Config setting or something instead. - static int rewindStateFreq = 0; // TODO: Any reason for this to be configurable? const static float rewindMaxWallFrequency = 1.0f; static float rewindLastTime = 0.0f; @@ -179,6 +188,11 @@ namespace SaveState Enqueue(Operation(SAVESTATE_REWIND, std::string(""), callback, cbUserData)); } + bool CanRewind() + { + return !rewindStates.Empty(); + } + // Slot utilities @@ -302,7 +316,7 @@ namespace SaveState static inline void CheckRewindState() { - if (gpuStats.numFlips % rewindStateFreq != 0) + if (gpuStats.numFlips % g_Config.iRewindFlipFrequency != 0) return; // For fast-forwarding, otherwise they may be useless and too close. @@ -317,7 +331,7 @@ namespace SaveState void Process() { - if (rewindStateFreq != 0) + if (g_Config.iRewindFlipFrequency != 0 && gpuStats.numFlips != 0) CheckRewindState(); if (!needsProcess) @@ -429,5 +443,6 @@ namespace SaveState pspFileSystem.MkDir("ms0:/PSP/PPSSPP_STATE"); std::lock_guard guard(mutex); + rewindStates.Clear(); } } diff --git a/Core/SaveState.h b/Core/SaveState.h index b2b9fda16d..65eccdb9ae 100644 --- a/Core/SaveState.h +++ b/Core/SaveState.h @@ -52,6 +52,9 @@ namespace SaveState // Warning: callback will be called on a different thread. void Rewind(Callback callback = 0, void *cbUserData = 0); + // Returns true if there are rewind snapshots available. + bool CanRewind(); + // Check if there's any save stating needing to be done. Normally called once per frame. void Process(); }; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index dc2ec89e63..b903fba78a 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -254,6 +254,7 @@ void GameSettingsScreen::CreateViews() { systemSettings->Add(new CheckBox(&g_Config.bSeparateIOThread, s->T("I/O on thread (experimental)")))->SetEnabled(!PSP_IsInited()); #endif systemSettings->Add(new PopupSliderChoice(&g_Config.iLockedCPUSpeed, 0, 1000, s->T("Change CPU Clock", "Change CPU Clock (0 = default)"), screenManager())); + systemSettings->Add(new PopupSliderChoice(&g_Config.iRewindFlipFrequency, 0, 1800, s->T("Rewind Snapshot Frequency (0 = off, mem hog)"), screenManager())); systemSettings->Add(new CheckBox(&g_Config.bAtomicAudioLocks, s->T("Atomic Audio locks (experimental)")))->SetEnabled(!PSP_IsInited()); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 4c11c98f4e..a13864e470 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -779,6 +779,12 @@ void GamePauseScreen::CreateViews() { loadStateButton_ = leftColumnItems->Add(new Choice(i->T("Load State"))); loadStateButton_->OnClick.Handle(this, &GamePauseScreen::OnLoadState); + if (g_Config.iRewindFlipFrequency > 0) { + UI::Choice *rewindButton = leftColumnItems->Add(new Choice(i->T("Rewind"))); + rewindButton->SetEnabled(SaveState::CanRewind()); + rewindButton->OnClick.Handle(this, &GamePauseScreen::OnRewind); + } + ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); root_->Add(rightColumn); @@ -833,6 +839,14 @@ UI::EventReturn GamePauseScreen::OnSaveState(UI::EventParams &e) { screenManager()->finishDialog(this, DR_CANCEL); return UI::EVENT_DONE; } + +UI::EventReturn GamePauseScreen::OnRewind(UI::EventParams &e) { + SaveState::Rewind(0, 0); + + screenManager()->finishDialog(this, DR_CANCEL); + return UI::EVENT_DONE; +} + UI::EventReturn GamePauseScreen::OnCwCheat(UI::EventParams &e) { screenManager()->push(new CwCheatScreen()); return UI::EVENT_DONE; diff --git a/UI/MainScreen.h b/UI/MainScreen.h index bdac77ca83..4bdcdb9065 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -72,6 +72,7 @@ private: UI::EventReturn OnSaveState(UI::EventParams &e); UI::EventReturn OnLoadState(UI::EventParams &e); + UI::EventReturn OnRewind(UI::EventParams &e); UI::EventReturn OnStateSelected(UI::EventParams &e); UI::EventReturn OnCwCheat(UI::EventParams &e);