mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 15:00:30 +02:00
sceGe: sceGeBreak(1) takes pending interrupts with it
Resetting the GE also gets rid of an interrupt that was raised but not taken yet, so a list that reached its FINISH just before never gets its finish callback. We delivered one anyway, for a list that no longer existed. If the break comes from inside a GE callback, the interrupt being handled is kept, since its handler still has to return. Found by gpu/ge/intrsuspend, which also confirms from a thread, with interrupts suspended, that nothing moves along the queue until the FINISH interrupt has been taken. Savestates: bump GPUCommon to 7. We didn't use to mark a PAUSE signal as delivered, which sceGeContinue now goes by, so a state saved with a list paused that way would load into a game that could never continue it. Fixed up on load. gpu/signals/handlercalls goes in as known failing: with an old SDK version, a stall address set from inside a SUSPEND callback doesn't reach the GE, which we can't express with just the one stall address per list. See docs/sceGe.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
41627cc0ce
commit
91c9c4d14e
5 files changed
+35
-3
No files matched your search
+12
-1
@@ -15,6 +15,7 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@@ -67,7 +68,8 @@ public:
|
||||
|
||||
bool run(PendingInterrupt& pend) override {
|
||||
if (ge_pending_cb.empty()) {
|
||||
ERROR_LOG_REPORT(Log::sceGe, "Unable to run GE interrupt: no pending interrupt");
|
||||
// sceGeBreak(1) got there first. If interrupts were off, this one had already been raised.
|
||||
DEBUG_LOG(Log::sceGe, "Ignoring GE interrupt, nothing pending anymore");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -291,6 +293,15 @@ bool __GeTriggerSync(GPUSyncType type, int id, u64 atTicks) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void __GeClearPendingInterrupts(bool interruptRunning) {
|
||||
if (interruptRunning && !ge_pending_cb.empty()) {
|
||||
ge_pending_cb.erase(std::next(ge_pending_cb.begin()), ge_pending_cb.end());
|
||||
} else {
|
||||
ge_pending_cb.clear();
|
||||
}
|
||||
CoreTiming::RemoveEvent(geInterruptEvent);
|
||||
}
|
||||
|
||||
bool __GeTriggerInterrupt(int listid, u32 pc, u64 atTicks) {
|
||||
GeInterruptData intrdata;
|
||||
intrdata.listid = listid;
|
||||
|
||||
@@ -52,6 +52,9 @@ void __GeDoState(PointerWrap &p);
|
||||
void __GeShutdown();
|
||||
bool __GeTriggerSync(GPUSyncType waitType, int id, u64 atTicks);
|
||||
bool __GeTriggerInterrupt(int listid, u32 pc, u64 atTicks);
|
||||
// Forgets the interrupts that haven't been delivered yet, as resetting the GE does.
|
||||
// The one being handled right now, if any, is still needed when its handler returns.
|
||||
void __GeClearPendingInterrupts(bool interruptRunning);
|
||||
void __GeWaitCurrentThread(GPUSyncType type, SceUID waitId, const char *reason);
|
||||
bool __GeTriggerWait(GPUSyncType type, SceUID waitId);
|
||||
|
||||
|
||||
+16
-1
@@ -579,7 +579,11 @@ u32 GPUCommon::Break(int mode) {
|
||||
{
|
||||
dls[i].state = PSP_GE_DL_STATE_NONE;
|
||||
dls[i].signal = PSP_GE_SIGNAL_NONE;
|
||||
dls[i].pendingInterrupt = false;
|
||||
}
|
||||
// This resets the GE, and an interrupt that hasn't been taken yet goes with it: no callback
|
||||
// for a list that reached its FINISH just before.
|
||||
__GeClearPendingInterrupts(interruptRunning);
|
||||
|
||||
nextListID = 0;
|
||||
currentList = NULL;
|
||||
@@ -1487,7 +1491,7 @@ struct DisplayList_v2 {
|
||||
};
|
||||
|
||||
void GPUCommon::DoState(PointerWrap &p) {
|
||||
auto s = p.Section("GPUCommon", 1, 6);
|
||||
auto s = p.Section("GPUCommon", 1, 7);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -1544,6 +1548,17 @@ void GPUCommon::DoState(PointerWrap &p) {
|
||||
dls[i].stackAddr = 0;
|
||||
}
|
||||
}
|
||||
if (s < 7 && p.mode == PointerWrap::MODE_READ) {
|
||||
// We didn't use to mark a PAUSE signal as delivered, which sceGeContinue now goes by.
|
||||
// Back then, a list was only PAUSED with that signal set once it had reached its FINISH,
|
||||
// and if the interrupt for that is still to come, it'll take care of this.
|
||||
for (DisplayList &dl : dls) {
|
||||
if (dl.state == PSP_GE_DL_STATE_PAUSED && dl.signal == PSP_GE_SIGNAL_HANDLER_PAUSE && !dl.pendingInterrupt) {
|
||||
dl.signal = PSP_GE_SIGNAL_HANDLER_SUSPEND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int currentID = 0;
|
||||
if (currentList != nullptr) {
|
||||
currentID = (int)(currentList - &dls[0]);
|
||||
|
||||
+1
-1
Submodule pspautotests updated: ba4d116e8c...c698aef2c0.
@@ -204,6 +204,7 @@ tests_good = [
|
||||
"gpu/ge/context",
|
||||
"gpu/ge/edram",
|
||||
"gpu/ge/enqueueparam",
|
||||
"gpu/ge/intrsuspend",
|
||||
"gpu/ge/queue",
|
||||
"gpu/ge/queue2",
|
||||
"gpu/primitives/indices",
|
||||
@@ -478,6 +479,8 @@ tests_next = [
|
||||
"gpu/reflection/reflection",
|
||||
"gpu/rendertarget/rendertarget",
|
||||
"gpu/signals/continue",
|
||||
# Old SDK: a stall update from inside a SUSPEND handler is remembered, but not applied to the GE. See docs/sceGe.md.
|
||||
"gpu/signals/handlercalls",
|
||||
"gpu/signals/jumps",
|
||||
"gpu/signals/simple",
|
||||
"gpu/simple/simple",
|
||||
|
||||
Reference in new issue
Block a user