mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
sceDisplay: Release vblank waiters ~50us after the vblank
On hardware a thread waiting for vblank returns ~53us after it, where we had it back in ~5us, and the first of four waiters runs after ~85us: each waiter beyond the first adds ~9us (pspautotests threads/scheduling/ vblankwake). The waiters are now released by a separate event 48us + 9us per extra waiter after the vblank. Which vblank a wait is for is still decided at the vblank, so a thread that starts waiting in between still waits a whole frame (sceDisplay section version 8). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
f26ca37dad
commit
02b2b7b699
2 files changed
+44
-12
No files matched your search
+43
-12
@@ -93,6 +93,9 @@ static bool framebufIsLatched;
|
||||
|
||||
static int enterVblankEvent = -1;
|
||||
static int leaveVblankEvent = -1;
|
||||
// Threads whose vblank wait is over, released a little after the vblank (see hleEnterVblank).
|
||||
static int vblankWakeEvent = -1;
|
||||
static std::vector<SceUID> vblankWakePending;
|
||||
static int afterFlipEvent = -1;
|
||||
static int lagSyncEvent = -1;
|
||||
|
||||
@@ -156,6 +159,7 @@ static u64 nextFlipCycles = 0;
|
||||
|
||||
void hleEnterVblank(u64 userdata, int cyclesLate);
|
||||
void hleLeaveVblank(u64 userdata, int cyclesLate);
|
||||
static void hleVblankWake(u64 userdata, int cyclesLate);
|
||||
void hleAfterFlip(u64 userdata, int cyclesLate);
|
||||
void hleLagSync(u64 userdata, int cyclesLate);
|
||||
|
||||
@@ -210,6 +214,8 @@ void __DisplayInit() {
|
||||
|
||||
enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank);
|
||||
leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank);
|
||||
vblankWakeEvent = CoreTiming::RegisterEvent("VBlankWake", &hleVblankWake);
|
||||
vblankWakePending.clear();
|
||||
afterFlipEvent = CoreTiming::RegisterEvent("AfterFlip", &hleAfterFlip);
|
||||
|
||||
lagSyncEvent = CoreTiming::RegisterEvent("LagSync", &hleLagSync);
|
||||
@@ -231,7 +237,7 @@ struct GPUStatistics_v0 {
|
||||
};
|
||||
|
||||
void __DisplayDoState(PointerWrap &p) {
|
||||
auto s = p.Section("sceDisplay", 1, 7);
|
||||
auto s = p.Section("sceDisplay", 1, 8);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -258,6 +264,14 @@ void __DisplayDoState(PointerWrap &p) {
|
||||
CoreTiming::RestoreRegisterEvent(leaveVblankEvent, "LeaveVBlank", &hleLeaveVblank);
|
||||
Do(p, afterFlipEvent);
|
||||
CoreTiming::RestoreRegisterEvent(afterFlipEvent, "AfterFlip", &hleAfterFlip);
|
||||
if (s >= 8) {
|
||||
Do(p, vblankWakeEvent);
|
||||
Do(p, vblankWakePending);
|
||||
} else {
|
||||
vblankWakeEvent = -1;
|
||||
vblankWakePending.clear();
|
||||
}
|
||||
CoreTiming::RestoreRegisterEvent(vblankWakeEvent, "VBlankWake", &hleVblankWake);
|
||||
|
||||
if (s >= 5) {
|
||||
Do(p, lagSyncEvent);
|
||||
@@ -316,6 +330,7 @@ void __DisplayDoState(PointerWrap &p) {
|
||||
|
||||
void __DisplayShutdown() {
|
||||
vblankWaitingThreads.clear();
|
||||
vblankWakePending.clear();
|
||||
}
|
||||
|
||||
void __DisplayVblankBeginCallback(SceUID threadID, SceUID prevCallbackId) {
|
||||
@@ -545,22 +560,21 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
|
||||
// Trigger VBlank interrupt handlers.
|
||||
__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR, PSP_INTR_SUB_ALL);
|
||||
|
||||
// Wake up threads waiting for VBlank
|
||||
u32 error;
|
||||
bool wokeThreads = false;
|
||||
// Threads waiting for this vblank are released about 48us after it, plus ~9us for each one
|
||||
// beyond the first: on hardware a lone waiter returns ~53us after the vblank (~58us with a
|
||||
// vblank handler registered), and with four the first to run does so after ~85us, where we'd
|
||||
// otherwise have it back in ~5us (pspautotests threads/scheduling/vblankwake). Which vblank a
|
||||
// wait is for is still decided here, so a thread that starts waiting in between waits for the
|
||||
// next one.
|
||||
for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
|
||||
if (--vblankWaitingThreads[i].vcountUnblock == 0) {
|
||||
// Only wake it if it wasn't already released by someone else.
|
||||
SceUID waitID = __KernelGetWaitID(vblankWaitingThreads[i].threadID, WAITTYPE_VBLANK, error);
|
||||
if (waitID == 1) {
|
||||
__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
|
||||
wokeThreads = true;
|
||||
}
|
||||
vblankWakePending.push_back(vblankWaitingThreads[i].threadID);
|
||||
vblankWaitingThreads.erase(vblankWaitingThreads.begin() + i--);
|
||||
}
|
||||
}
|
||||
if (wokeThreads) {
|
||||
__KernelReSchedule("entered vblank");
|
||||
if (!vblankWakePending.empty()) {
|
||||
const int releaseUs = 48 + 9 * ((int)vblankWakePending.size() - 1);
|
||||
CoreTiming::ScheduleEvent(usToCycles(releaseUs) - cyclesLate, vblankWakeEvent, 0);
|
||||
}
|
||||
|
||||
// We use the emulation timebase here, for auto movements to be smooth as seen from the game.
|
||||
@@ -756,6 +770,23 @@ void hleAfterFlip(u64 userdata, int cyclesLate) {
|
||||
}
|
||||
}
|
||||
|
||||
static void hleVblankWake(u64 userdata, int cyclesLate) {
|
||||
u32 error;
|
||||
bool wokeThreads = false;
|
||||
for (SceUID threadID : vblankWakePending) {
|
||||
// Only wake it if it wasn't already released by someone else.
|
||||
SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_VBLANK, error);
|
||||
if (waitID == 1) {
|
||||
__KernelResumeThreadFromWait(threadID, 0);
|
||||
wokeThreads = true;
|
||||
}
|
||||
}
|
||||
vblankWakePending.clear();
|
||||
if (wokeThreads) {
|
||||
__KernelReSchedule("vblank waiters released");
|
||||
}
|
||||
}
|
||||
|
||||
void hleLeaveVblank(u64 userdata, int cyclesLate) {
|
||||
flippedThisFrame = false;
|
||||
VERBOSE_LOG(Log::sceDisplay,"Leave VBlank %i", (int)userdata - 1);
|
||||
|
||||
Reference in new issue
Block a user