CoreTiming::UnscheduleEvent returns the scheduled time minus the current time,
which is negative when the event is overdue but hasn't been processed yet - the
exact situation when a wait is satisfied right around its own timeout. Only the
semaphore clamped it; everywhere else we wrote (u32)cyclesToUs(negative) into
the game's timeout variable, i.e. a huge bogus "remaining time".
Pulled the shared shape into HLEKernel::WriteRemainingTimeout so it can't drift
apart again - event flags, mbx, fpl, vpl, msgpipe, mutex, lwmutex and semaphore
all go through it now. The two thread-end sites keep their own copy since they
unschedule even when the game passed no timeout pointer, and sceUsb just gets
the clamp.
A delay of 0 never reschedules, actually, unless there's a better priority
thread. A greater delay depends on if a >= or similar check matches, but
generally is at least 200us.
It seems like this actually completes in at least another 10-20us.
Turns out it's a deadline to wake up by, which makes sense.
sceDisplayWaitVblankStartMultiCB 10
-> Callback
sceDisplayWaitVblankStartMultiCB 2
+2 vblanks
<- Thread
+8 more vblanks