mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
sceGe: what callbacks see, and a full GE reset on sceKernelLoadExec
From another pass over ge.prx against our code, each checked on a PSP with gpu/ge/callbackstate except the last: - A list's context is restored after its finish callback, which sees the state the list left. We restored at the FINISH, before it. Now that nothing runs until InterruptEnd(), that's where it happens. - sceGeSaveContext/RestoreContext only fail while the GE is executing. It's stopped during a finish callback and a SUSPEND signal callback, however much is queued, so they work there. We said busy whenever a list existed. - sceGeListDeQueue emptying the queue doesn't turn completed lists into nothing, only sceGeDrawSync does. CheckDrawSync() is gone. - The "break in progress" flag that makes sceGeContinue only requeue the list is cleared by an interrupt that follows the break at once, so it's only seen from a callback or with interrupts off. Ours lasted until the next GE interrupt of any kind. - sceKernelLoadExec restarts the GE driver, which begins by zeroing every register and matrix. Reinitialize() now does too, so a program started that way finds the same GE as one booted directly, rather than its launcher's. Not testable on hardware: nothing after the restart can report back. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
e47851a069
commit
a36d09daac
4 files changed
+35
-18
No files matched your search
+33
-16
@@ -94,6 +94,16 @@ void GPUCommon::Reinitialize() {
|
||||
// behind the first list of the new executable. Crazy Taxi: Fare Wars starts its games this way, #19894.
|
||||
dlQueue.clear();
|
||||
|
||||
// The GE driver starts over as well, and the first thing it does is run a list that sets every
|
||||
// register to zero, and all the matrices. So a program started this way finds the GE exactly as
|
||||
// one booted directly does, not as the previous one left it.
|
||||
gstate.Reset();
|
||||
gstate_c.offsetAddr = 0;
|
||||
gstate_c.vertexAddr = 0;
|
||||
gstate_c.indexAddr = 0;
|
||||
ResetMatrices();
|
||||
gstate_c.Dirty(DIRTY_ALL);
|
||||
|
||||
nextListID = 0;
|
||||
currentList = nullptr;
|
||||
interruptRunning = false;
|
||||
@@ -152,6 +162,15 @@ void GPUCommon::PopDLQueue() {
|
||||
}
|
||||
|
||||
bool GPUCommon::BusyDrawing() {
|
||||
// This is about whether the GE is executing right now, not whether lists are queued. It's
|
||||
// stopped for the duration of a finish callback, and of a signal callback unless that's a
|
||||
// CONTINUE one, which the GE doesn't wait for. sceGeSaveContext works fine from those.
|
||||
if (interruptRunning) {
|
||||
const bool continueSignal = gpuState == GPUSTATE_INTERRUPT && currentList && currentList->signal == PSP_GE_SIGNAL_HANDLER_CONTINUE;
|
||||
if (!continueSignal)
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 state = DrawSync(1);
|
||||
if (state == PSP_GE_LIST_DRAWING || state == PSP_GE_LIST_STALLING) {
|
||||
if (currentList && currentList->state != PSP_GE_DL_STATE_PAUSED) {
|
||||
@@ -222,13 +241,6 @@ u32 GPUCommon::DrawSync(int mode) {
|
||||
return PSP_GE_LIST_DRAWING;
|
||||
}
|
||||
|
||||
void GPUCommon::CheckDrawSync() {
|
||||
if (dlQueue.empty()) {
|
||||
for (int i = 0; i < DisplayListMaxCount; ++i)
|
||||
dls[i].state = PSP_GE_DL_STATE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
int GPUCommon::ListSync(int listid, int mode) {
|
||||
gpuStats.perFrame.numListSyncs++;
|
||||
|
||||
@@ -506,7 +518,7 @@ u32 GPUCommon::DequeueList(int listid) {
|
||||
dl.waitUntilTicks = 0;
|
||||
__GeTriggerWait(GPU_SYNC_LIST, listid);
|
||||
|
||||
CheckDrawSync();
|
||||
// Completed lists stay completed, even if this empties the queue. Only sceGeDrawSync recycles them.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -635,7 +647,10 @@ u32 GPUCommon::Break(int mode) {
|
||||
currentList->interrupted = true;
|
||||
currentList->state = PSP_GE_DL_STATE_PAUSED;
|
||||
currentList->signal = PSP_GE_SIGNAL_HANDLER_SUSPEND;
|
||||
isbreak = true;
|
||||
// On hardware, the break sets off a finish interrupt of its own, and until that has been taken,
|
||||
// sceGeContinue only marks the list to be started by it. From a thread that's immediate, so this
|
||||
// is only ever seen from a callback, or with interrupts off. InterruptEnd() is where it ends.
|
||||
isbreak = __IsInInterrupt() || !__InterruptsEnabled();
|
||||
|
||||
return currentList->id;
|
||||
}
|
||||
@@ -1217,17 +1232,19 @@ void GPUCommon::Execute_End(u32 op, u32 diff) {
|
||||
FlushImm();
|
||||
currentList->subIntrToken = prev & 0xFFFF;
|
||||
UpdateState(GPUSTATE_DONE);
|
||||
// Since we marked done, we have to restore the context now before the next list runs.
|
||||
if (currentList->started && currentList->context.IsValid()) {
|
||||
gstate.Restore(currentList->context);
|
||||
ReapplyGfxState();
|
||||
// Don't restore the context again.
|
||||
currentList->started = false;
|
||||
}
|
||||
|
||||
if (currentList->interruptsEnabled && __GeTriggerInterrupt(currentList->id, currentList->pc, startingTicks + cyclesExecuted)) {
|
||||
// The context is restored once the finish callback has run, which gets to see the state
|
||||
// the list left behind. Nothing else runs before then, see InterruptEnd().
|
||||
currentList->pendingInterrupt = true;
|
||||
} else {
|
||||
// No interrupt to wait for, so this is it.
|
||||
if (currentList->started && currentList->context.IsValid()) {
|
||||
gstate.Restore(currentList->context);
|
||||
ReapplyGfxState();
|
||||
// Don't restore the context again.
|
||||
currentList->started = false;
|
||||
}
|
||||
currentList->state = PSP_GE_DL_STATE_COMPLETED;
|
||||
currentList->waitUntilTicks = startingTicks + cyclesExecuted;
|
||||
busyTicks = std::max(busyTicks, currentList->waitUntilTicks);
|
||||
|
||||
@@ -424,5 +424,4 @@ protected:
|
||||
private:
|
||||
void DoExecuteCall(u32 target);
|
||||
void PopDLQueue();
|
||||
void CheckDrawSync();
|
||||
};
|
||||
+1
-1
Submodule pspautotests updated: c698aef2c0...830c78b5bd.
Reference in new issue
Block a user