From 130ea9ced9b4d8775e19ec3403a308debdbfb830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 19 Oct 2026 20:54:29 +0200 Subject: [PATCH] GPU: Save the next display list id, and restart frame dumps on load - Which list slot and id the next sceGeListEnQueue gets wasn't saved, so after a load it depended on the session before it, and replays and rewinds diverged. Older states start it at 0, and also get the default EDRAM translation and visible matrices they don't carry, rather than the previous session's. - A frame dump recording during a load replayed the loaded state's commands on the registers from before it. It now starts over from the loaded state. Co-Authored-By: Claude Opus 5.5 (1M context) --- GPU/Debugger/Record.cpp | 16 ++++++++++++++++ GPU/Debugger/Record.h | 3 +++ GPU/GPUCommon.cpp | 19 ++++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index b0ab2efd1e..a2c42e6bbc 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -695,6 +695,22 @@ bool Recorder::RecordNextFrame(const std::function callback) return true; } +void Recorder::RestartAfterLoad() { + if (!active) { + return; + } + NOTICE_LOG(Log::System, "Savestate loaded, restarting the recording"); + commands.clear(); + pushbuf.clear(); + lastVRAM.clear(); + lastEdramTrans = 0x400; + flipLastAction = gpuStats.totals.numFlips; + flipFinishAt = -1; + std::lock_guard guard(callbackLock_); + active = false; + nextFrame = true; +} + void Recorder::FinishRecording() { // We're done - this was just to write the result out. if (!active) { diff --git a/GPU/Debugger/Record.h b/GPU/Debugger/Record.h index 78964859b0..ea911be628 100644 --- a/GPU/Debugger/Record.h +++ b/GPU/Debugger/Record.h @@ -50,6 +50,9 @@ public: return nextFrame || active; } bool RecordNextFrame(const std::function callback); + // A savestate load replaced everything a recording in progress started from. It starts over + // from the loaded state, at the next frame. + void RestartAfterLoad(); void ClearCallback() { std::lock_guard guard(callbackLock_); writeCallback = nullptr; diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 7372fc989d..913ada9910 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1953,7 +1953,7 @@ struct DisplayList_v2 { }; void GPUCommon::DoState(PointerWrap &p) { - auto s = p.Section("GPUCommon", 1, 7); + auto s = p.Section("GPUCommon", 1, 8); if (!s) return; @@ -2056,9 +2056,26 @@ void GPUCommon::DoState(PointerWrap &p) { if (s >= 5) { Do(p, matrixVisible.all); + } else if (p.mode == PointerWrap::MODE_READ) { + ResetMatrices(); } if (s >= 6) { Do(p, edramTranslation_); + } else if (p.mode == PointerWrap::MODE_READ) { + edramTranslation_ = 0x400; + } + // Which slot (and so which id) the next list gets. Older states start over at 0, rather than + // carry on from the session before the load. + if (s >= 8) { + Do(p, nextListID); + } else { + nextListID = 0; + } + if (p.mode == PointerWrap::MODE_READ) { + if (nextListID < 0 || nextListID > DisplayListMaxCount) { + nextListID = 0; + } + recorder_.RestartAfterLoad(); } }