Zelda64Recomp/patches/input_latency.c

209 lines
6.7 KiB
C

#include "patches.h"
#include "sys_cfb.h"
#include "buffers.h"
#include "fault.h"
#include "z64speed_meter.h"
void recomp_set_current_frame_poll_id();
void PadMgr_HandleRetrace(void);
void PadMgr_LockPadData(void);
void PadMgr_UnlockPadData(void);
void PadMgr_ThreadEntry() {
// @recomp Controller polling was moved to the main thread, so there's nothing to do here.
}
// @recomp Patched to do the actual input polling.
void PadMgr_GetInput(Input* inputs, s32 gameRequest) {
// @recomp Do an actual poll if gameRequest is true.
if (gameRequest) {
PadMgr_HandleRetrace();
// @recomp Tag the current frame's input polling id for latency tracking.
recomp_set_current_frame_poll_id();
}
PadMgr_LockPadData();
PadMgr_GetInputNoLock(inputs, gameRequest);
PadMgr_UnlockPadData();
}
// @recomp Just call PadMgr_GetInput.
void PadMgr_GetInput2(Input* inputs, s32 gameRequest) {
PadMgr_GetInput(inputs, gameRequest);
}
extern CfbInfo sGraphCfbInfos[3];
u32 recomp_time_us();
void recomp_measure_latency();
OSMesgQueue *rdp_queue_ptr = NULL;
// @recomp Immediately sends the graphics task instead of queueing it in the scheduler.
void Graph_TaskSet00(GraphicsContext* gfxCtx, GameState* gameState) {
static s32 retryCount = 10;
static s32 cfbIdx = 0;
OSTask_t* task = &gfxCtx->task.list.t;
OSScTask* scTask = &gfxCtx->task;
OSTimer timer;
OSMesg msg;
CfbInfo* cfb;
// @recomp Additional static members for extra scheduling purposes.
static IrqMgrClient irq_client = {0};
static OSMesgQueue vi_queue = {0};
static OSMesg vi_buf[8] = {0};
static OSMesgQueue rdp_queue = {0};
static OSMesg rdp_mesg = NULL;
static bool created = false;
if (!created) {
created = true;
osCreateMesgQueue(&vi_queue, vi_buf, ARRAY_COUNT(vi_buf));
osCreateMesgQueue(&rdp_queue, &rdp_mesg, 1);
extern IrqMgr gIrqMgr;
IrqMgr_AddClient(&gIrqMgr, &irq_client, &vi_queue);
}
// @recomp Disable the wait here so that it can be moved after task submission for minimizing latency.
// retry:
// osSetTimer(&timer, OS_USEC_TO_CYCLES(3 * 1000 * 1000), 0, &gfxCtx->queue, (OSMesg)666);
u32 count_before = recomp_time_us();
osRecvMesg(&gfxCtx->queue, &msg, OS_MESG_BLOCK);
u32 count_after = recomp_time_us();
// osStopTimer(&timer);
// if (msg == (OSMesg)666) {
// osSyncPrintf("GRAPH SP TIMEOUT\n");
// if (retryCount >= 0) {
// retryCount--;
// Sched_SendGfxCancelMsg(&gSchedContext);
// goto retry;
// } else {
// // graph.c: No more! die!
// osSyncPrintf("graph.c:もうダメ!死ぬ!\n");
// Fault_AddHungupAndCrashImpl("RCP is HUNG UP!!", "Oh! MY GOD!!");
// }
// }
gfxCtx->masterList = gGfxMasterDL;
if (gfxCtx->callback != NULL) {
gfxCtx->callback(gfxCtx, gfxCtx->callbackArg);
}
task->type = M_GFXTASK;
task->flags = OS_SC_DRAM_DLIST;
task->ucodeBoot = SysUcode_GetUCodeBoot();
task->ucodeBootSize = SysUcode_GetUCodeBootSize();
task->ucode = SysUcode_GetUCode();
task->ucodeData = SysUcode_GetUCodeData();
task->ucodeSize = SP_UCODE_SIZE;
task->ucodeDataSize = SP_UCODE_DATA_SIZE;
task->dramStack = (u64*)gGfxSPTaskStack;
task->dramStackSize = sizeof(gGfxSPTaskStack);
task->outputBuff = gGfxSPTaskOutputBufferPtr;
task->outputBuffSize = gGfxSPTaskOutputBufferEnd;
task->dataPtr = (u64*)gGfxMasterDL;
task->dataSize = 0;
task->yieldDataPtr = (u64*)gGfxSPTaskYieldBuffer;
task->yieldDataSize = sizeof(gGfxSPTaskYieldBuffer);
scTask->next = NULL;
scTask->flags = OS_SC_RCP_MASK | OS_SC_SWAPBUFFER | OS_SC_LAST_TASK;
if (SREG(33) & 1) {
SREG(33) &= ~1;
scTask->flags &= ~OS_SC_SWAPBUFFER;
gfxCtx->framebufferIndex--;
}
scTask->msgQ = &gfxCtx->queue;
scTask->msg = NULL;
{ s32 pad; }
cfb = &sGraphCfbInfos[cfbIdx];
cfbIdx = (cfbIdx + 1) % ARRAY_COUNT(sGraphCfbInfos);
cfb->fb1 = gfxCtx->curFrameBuffer;
cfb->swapBuffer = gfxCtx->curFrameBuffer;
if (gfxCtx->updateViMode) {
gfxCtx->updateViMode = false;
cfb->viMode = gfxCtx->viMode;
cfb->features = gfxCtx->viConfigFeatures;
cfb->xScale = gfxCtx->xScale;
cfb->yScale = gfxCtx->yScale;
} else {
cfb->viMode = NULL;
}
cfb->unk_10 = 0;
cfb->updateRate = gameState->framerateDivisor;
scTask->framebuffer = cfb;
while (gfxCtx->queue.validCount != 0) {
osRecvMesg(&gfxCtx->queue, NULL, OS_MESG_NOBLOCK);
}
// @recomp Set up the dedicated RDP complete message queue pointer for the scheduler.
rdp_queue_ptr = &rdp_queue;
gfxCtx->schedMsgQ = &gSchedContext.cmdQ;
recomp_measure_latency();
osSendMesg(&gSchedContext.cmdQ, scTask, OS_MESG_BLOCK);
Sched_SendEntryMsg(&gSchedContext);
// @recomp Wait for the RDP complete message to mitigate waiting between creating the next task and submitting it.
osRecvMesg(&rdp_queue, NULL, OS_MESG_BLOCK);
rdp_queue_ptr = NULL;
// @recomp Manually wait the required number of VI periods after submitting the task
// so that the next frame doesn't need to wait before submitting its task.
u32 vi_count_before = recomp_time_us();
for (int i = 0; i < cfb->updateRate; i++) {
osRecvMesg(&vi_queue, NULL, OS_MESG_BLOCK);
}
// @recomp Flush any excess VI messages that came in.
while (osRecvMesg(&vi_queue, NULL, OS_MESG_NOBLOCK) == 0) {
;
}
u32 vi_count_after = recomp_time_us();
// recomp_printf("recv wait added %d us\nVI wait: %d us\n", count_after - count_before, vi_count_after - vi_count_before);
}
extern OSTime sRDPStartTime;
// @recomp Patched to send a message to the dedicated RDP complete message queue.
void Sched_HandleRDPDone(SchedContext* sched) {
OSScTask* curRDP;
OSScTask* nextRSP = NULL;
OSScTask* nextRDP = NULL;
s32 state;
if (sched->curRDPTask == NULL) {
osSyncPrintf("__scHandleRDP:sc->curRDPTask == NULL\n");
return;
}
// Log run time
gRDPTimeAcc = osGetTime() - sRDPStartTime;
// Mark task done
curRDP = sched->curRDPTask;
sched->curRDPTask = NULL;
curRDP->state &= ~OS_SC_DP;
// @recomp Send a message to the dedicated RDP complete message queue if it's currently set up.
if (rdp_queue_ptr) {
osSendMesg(rdp_queue_ptr, NULL, OS_MESG_BLOCK);
}
Sched_NotifyDone(sched, curRDP);
// Schedule and run next task
state = ((sched->curRSPTask == NULL) << 1) | (sched->curRDPTask == NULL);
if (Sched_Schedule(sched, &nextRSP, &nextRDP, state) != state) {
Sched_RunTask(sched, nextRSP, nextRDP);
}
}