From 507bb0801b05fabeab9b6d9469a3b89f46b42287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 Sep 2026 14:52:50 -0600 Subject: [PATCH] GE: Complete lists dropped on error, flush before immediate draws - A list dropped for a bad pc or a GE error stayed RUNNING: its ID was never freed and sceGeListSync on it never returned. Complete it like a finished one. - FlushImm switches to through mode and another vertex decoder, so flush the queued draws first even when the immediate flags match. - Clear leftover temporary GE breakpoints when setting or clearing the next break, so a step that never got there doesn't trip later. Co-Authored-By: Claude Opus 5.5 (1M context) --- GPU/GPUCommon.cpp | 26 ++++++++++++++++++++++++-- GPU/GPUCommon.h | 1 + 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 0bd1aadab7..67a1ea807f 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -957,6 +957,7 @@ DLResult GPUCommon::ProcessDLQueue() { // Nothing to execute here, and leaving it at the head of the queue would block everything // behind it for good. Treat it like a list that ran into an error. ERROR_LOG(Log::G3D, "Display list %d has a bad pc %08x (state %d), dropping it", listIndex, list.pc, (int)list.state); + CompleteFailedList(list); dlQueue.erase(std::remove(dlQueue.begin(), dlQueue.end(), listIndex), dlQueue.end()); continue; } @@ -1045,7 +1046,8 @@ DLResult GPUCommon::ProcessDLQueue() { } break; case GPUSTATE_ERROR: - // don't do anything - though dunno about error... + // The list can't continue. It's removed from the queue below. + CompleteFailedList(list); break; case GPUSTATE_STALL: // Resume work on this same display list later. The GE is still busy with what it has @@ -1390,6 +1392,20 @@ void GPUCommon::Execute_End(u32 op, u32 diff) { } } +// A list dropped on an error still has to complete like a finished one, or its ID is never freed and +// sceGeListSync on it never returns. +void GPUCommon::CompleteFailedList(DisplayList &list) { + if (list.started && list.context.IsValid()) { + gstate.Restore(list.context); + ReapplyGfxState(); + list.started = false; + } + list.state = PSP_GE_DL_STATE_COMPLETED; + list.waitUntilTicks = startingTicks + cyclesExecuted; + busyTicks = std::max(busyTicks, list.waitUntilTicks); + __GeTriggerSync(GPU_SYNC_LIST, list.id, list.waitUntilTicks); +} + void GPUCommon::Execute_BoundingBox(u32 op, u32 diff) { // Just resetting, nothing to check bounds for. const u32 count = op & 0xFFFF; @@ -1552,8 +1568,10 @@ void GPUCommon::FlushImm() { bool changed = texturing != prevTexturing || cullEnable != prevCullEnable || dither != prevDither; changed = changed || prevShading != shading || prevFog != fog; + // Always flush, even if the flags match: DispatchSubmitImm switches to through mode and a different + // vertex decoder, which would otherwise apply to the draws already queued. + Flush(); if (changed) { - Flush(); gstate.antiAliasEnable = (GE_CMD_ANTIALIASENABLE << 24) | (int)antialias; gstate.shademodel = (GE_CMD_SHADEMODE << 24) | (int)shading; gstate.cullfaceEnable = (GE_CMD_CULLFACEENABLE << 24) | (int)cullEnable; @@ -2290,12 +2308,16 @@ bool GPUCommon::NeedsSlowInterpreter() const { void GPUCommon::ClearBreakNext() { breakNext_ = GPUDebug::BreakNext::NONE; breakAtCount_ = -1; + // A step that never reached its target leaves these behind, and they'd trip unexpectedly later. + breakpoints_.ClearTempBreakpoints(); GPUStepping::ResumeFromStepping(); } void GPUCommon::SetBreakNext(GPUDebug::BreakNext next) { breakNext_ = next; breakAtCount_ = -1; + // Drop the ones from a previous step that didn't get there, before adding this one's. + breakpoints_.ClearTempBreakpoints(); switch (next) { case GPUDebug::BreakNext::TEX: breakpoints_.AddTextureChangeTempBreakpoint(); diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 384a18102f..a295a970c3 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -452,4 +452,5 @@ protected: private: void DoExecuteCall(u32 target); void PopDLQueue(); + void CompleteFailedList(DisplayList &list); };