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) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-19 20:54:29 +02:00
1 parent 814593ee4d
commit 130ea9ced9
3 files changed
+37 -1

No files matched your search

+16
View File
@@ -695,6 +695,22 @@ bool Recorder::RecordNextFrame(const std::function<void(const Path &)> 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<std::mutex> guard(callbackLock_);
active = false;
nextFrame = true;
}
void Recorder::FinishRecording() {
// We're done - this was just to write the result out.
if (!active) {
+3
View File
@@ -50,6 +50,9 @@ public:
return nextFrame || active;
}
bool RecordNextFrame(const std::function<void(const Path &)> 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<std::mutex> guard(callbackLock_);
writeCallback = nullptr;
+18 -1
View File
@@ -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();
}
}