D248: fix port-wide 30fps halving -- pointer-width scheduler ABI bug

osScAddClient() stashes a per-client "every retrace vs every other
retrace" flag in the next array slot's .next field (c[1].next = next);
__scHandleRetrace read it back via a hardcoded 8-byte offset that only
lands on the right memory when OSScClient is 8 bytes (32-bit
pointers). On this 64-bit port the struct is 16 bytes, so the read
landed inside the gfx client's own msgQ pointer (always non-NULL)
instead, silently collapsing the gfx client onto the audio client's
30Hz path even though the VI-retrace pacemaker itself was always
correctly ticking at 60Hz.

Fix indexes the struct (client[1].next) instead of hand-rolling the
byte offset, so it scales with pointer width on either platform --
same class as D122/D126/D132/D209, just in the scheduler instead of a
ROM-serialized struct. Verified via the Video.DisplayFPS overlay:
30 FPS pre-fix, 60 FPS post-fix, same build/scene (-level_09).
-level_09/-level_20 both crash-free post-fix.
This commit is contained in:
James Dansereau committed 2026-09-12 21:36:21 -04:00
1 parent dac4ae7c0c
commit 52477b44af
4 files changed
+42

No files matched your search

+17
View File
@@ -331,7 +331,24 @@ void __scHandleRetrace(OSSched *sc) {
__scExec(sc, sp, dp);
}
for (client = sc->clientList; client != 0; client = client->next) {
/* D2xx (PC-port pointer-width fix): osScAddClient() stashes a
* per-client "every-other-frame" flag in the *next* array slot
* (`c[1].next = next`, see osScAddClient above) -- gfxClient gets
* NULL (send every retrace, 60Hz) and g_AudioClient gets a nonzero
* sentinel (send every other retrace, 30Hz). This read has to land
* on that same slot. The N64 line below finds it via a hardcoded
* 2*sizeof(s32)=8-byte offset, which only equals sizeof(OSScClient)
* (and thus &client[1]) when pointers are 32-bit; on this 64-bit
* port sizeof(OSScClient)==16, so the hardcoded offset instead reads
* the low 32 bits of client->msgQ itself (always non-NULL) and the
* gfx client silently loses its 60Hz path, halving to 30fps. Fix:
* index the real field instead of hardcoding the byte offset -- it
* naturally scales with the pointer width on either platform. */
#ifdef PORT
if ((client[1].next == 0) || ((sc->frameCount & 1) == 0)) {
#else
if ((*((s32*)client + 2) == 0) || ((sc->frameCount & 1) == 0)) {
#endif
osSendMesg(client->msgQ, (OSMesg) &sc->retraceMsg, OS_MESG_NOBLOCK);
}
}