Files
perfect-dark/src/lib/sched.c
T
2023-05-03 23:01:00 +10:00

365 lines
8.7 KiB
C

#include <ultra64.h>
#include "lib/boot.h"
#include "lib/sched.h"
#include "constants.h"
#include "game/menugfx.h"
#include "bss.h"
#include "lib/args.h"
#include "lib/audiomgr.h"
#include "lib/reset.h"
#include "lib/rzip.h"
#include "lib/main.h"
#include "lib/snd.h"
#include "lib/pimgr.h"
#include "lib/profile.h"
#include "lib/lib_48150.h"
#include "lib/vi.h"
#include "lib/joy.h"
#include "data.h"
#include "types.h"
/*
* OSScTask state
*/
#define OS_SC_YIELD 0x0010 /* set if yield requested */
#define OS_SC_YIELDED 0x0020 /* set if yield completed */
OSSched g_Sched;
OSViMode var8008dcc0[2];
OSViMode *var8008dd60[2];
OSViMode var8008dd68[2];
u32 var8008de08;
s32 var8008de0c;
s32 var8008de10;
u32 var8008de14;
OSTimer g_SchedRspTimer;
u8 g_ScBottleneck = ' ';
s32 var8005ce74 = 0;
f32 g_ViXScalesBySlot[2] = {1, 1};
f32 g_ViYScalesBySlot[2] = {1, 1};
u32 g_SchedViModesPending[2] = {false, false};
s32 g_ViUnblackTimer = 3;
s32 g_ViShakeDirection = 1;
s32 g_ViShakeIntensity = 0;
s32 g_ViShakeTimer = 0;
u32 var8005cea0 = 0;
u32 var8005cea4 = 0;
OSScMsg g_SchedRspMsg = {OS_SC_RSP_MSG};
bool g_SchedIsFirstTask = true;
static void __scExec(OSSched *sc, OSScTask *t)
{
if (t->list.t.type == M_GFXTASK) {
if ((t->state & OS_SC_YIELD) == 0) {
profileHandleRspEvent(RSPEVENT_GFX_START);
}
} else {
osWritebackDCacheAll();
profileHandleRspEvent(RSPEVENT_AUD_START);
}
t->state &= ~(OS_SC_YIELD | OS_SC_YIELDED);
osSpTaskLoad(&t->list);
osSpTaskStartGo(&t->list);
sc->curRSPTask = t;
if (t->list.t.type == M_GFXTASK) {
sc->curRDPTask = t;
}
}
static void __scTryDispatch(OSSched *sc)
{
if (sc->curRSPTask == NULL) {
if (sc->nextAudTask) {
OSScTask *t = sc->nextAudTask;
sc->nextAudTask = NULL;
__scExec(sc, t);
} else if (sc->curRDPTask == NULL && sc->queuedFB == NULL) {
OSScTask *t = sc->nextGfxTask;
if (t) {
sc->nextGfxTask = sc->nextGfxTask2;
sc->nextGfxTask2 = NULL;
__scExec(sc, t);
}
}
}
}
static void __scSwap(OSSched *sc, void *framebuffer)
{
if (g_SchedIsFirstTask) {
osViBlack(false);
g_SchedIsFirstTask = false;
}
var8005ce74 = (var8005ce74 + 1) % 2;
if (g_SchedViModesPending[1 - var8005ce74]) {
if (var8008dd60[1 - var8005ce74]->comRegs.width != var8008dcc0[1 - var8005ce74].comRegs.width
|| var8008dd60[1 - var8005ce74]->comRegs.xScale != var8008dcc0[1 - var8005ce74].comRegs.xScale
|| var8008dd60[1 - var8005ce74]->fldRegs[0].yScale != var8008dcc0[1 - var8005ce74].fldRegs[0].yScale
|| var8008dd60[1 - var8005ce74]->fldRegs[1].yScale != var8008dcc0[1 - var8005ce74].fldRegs[1].yScale
|| var8008dd60[1 - var8005ce74]->fldRegs[0].origin != var8008dcc0[1 - var8005ce74].fldRegs[0].origin
|| var8008dd60[1 - var8005ce74]->fldRegs[1].origin != var8008dcc0[1 - var8005ce74].fldRegs[1].origin) {
s32 mask = osSetIntMask(0x80401);
*var8008dd60[1 - var8005ce74] = var8008dcc0[1 - var8005ce74];
osSetIntMask(mask);
osViSetMode(var8008dd60[1 - var8005ce74]);
osViBlack(g_ViUnblackTimer);
osViSetXScale(g_ViXScalesBySlot[1 - var8005ce74]);
osViSetYScale(g_ViYScalesBySlot[1 - var8005ce74]);
#ifdef ANTIALIAS
osViSetSpecialFeatures(OS_VI_GAMMA_OFF | OS_VI_DITHER_FILTER_ON);
#else
osViSetSpecialFeatures(OS_VI_GAMMA_OFF | OS_VI_DITHER_FILTER_OFF);
#endif
}
g_SchedViModesPending[1 - var8005ce74] = false;
}
if (g_ViUnblackTimer != 0 && g_ViUnblackTimer < 3) {
g_ViUnblackTimer--;
}
osViSwapBuffer(framebuffer);
}
static void __scTaskComplete(OSSched *sc, OSScTask *t)
{
if (t->list.t.type == M_AUDTASK) {
profileHandleRspEvent(RSPEVENT_AUD_FINISH);
osSendMesg(t->msgQ, t->msg, OS_MESG_BLOCK);
} else {
profileHandleRspEvent(RSPEVENT_GFX_FINISH);
if (sc->scheduledFB == NULL) {
sc->scheduledFB = t->framebuffer;
__scSwap(sc, t->framebuffer);
} else {
sc->queuedFB = t->framebuffer;
}
osSendMesg(t->msgQ, t->msg, OS_MESG_BLOCK);
}
}
//-----------------------------------------------------------------------------\
//-- Event handlers -----------------------------------------------------------/
//----------------------------------------------------------------------------/
static void __scHandleRetrace(OSSched *sc)
{
if (sc->scheduledFB && osViGetCurrentFramebuffer() == sc->scheduledFB) {
if (sc->queuedFB) {
sc->scheduledFB = sc->queuedFB;
sc->queuedFB = NULL;
__scSwap(sc, sc->scheduledFB);
__scTryDispatch(sc);
} else {
sc->scheduledFB = NULL;
}
}
sc->alt ^= 1;
/**
* On every second retrace, we set a ~6ms timer to prompt the audio thread
* to start making another frame. When the audio task is ready, we try to
* give it to the RSP after the next gfx task finishes rather than yield the
* gfx task. But if we're at the second retrace and the previous gfx task is
* still not done then we must yield it.
*/
if (sc->alt && !g_Resetting) {
osStopTimer(&g_SchedRspTimer);
osSetTimer(&g_SchedRspTimer, 280000, 0, amgrGetFrameMesgQueue(), &g_SchedRspMsg);
if (sc->nextAudTask && sc->curRSPTask->list.t.type == M_GFXTASK) {
osSpTaskYield();
sc->curRSPTask->state |= OS_SC_YIELD;
}
}
if (!g_Resetting) {
vi00009ed4();
}
joysTick();
if (sc->gfxmq) {
osSendMesg(sc->gfxmq, (OSMesg) &sc->retraceMsg, OS_MESG_NOBLOCK);
}
}
static void __scHandleRSP(OSSched *sc)
{
if (!g_Resetting) {
OSScTask *t = sc->curRSPTask;
sc->curRSPTask = NULL;
if ((t->state & OS_SC_YIELD) && osSpTaskYielded(&t->list)) {
t->state |= OS_SC_YIELDED;
sc->nextGfxTask2 = sc->nextGfxTask;
sc->nextGfxTask = t;
sc->curRDPTask = NULL;
} else {
t->state &= ~OS_SC_NEEDS_RSP;
if ((t->state & OS_SC_RCP_MASK) == 0) {
__scTaskComplete(sc, t);
}
}
__scTryDispatch(sc);
}
}
static void __scHandleRDP(OSSched *sc)
{
OSScTask *t;
schedUpdatePendingArtifacts();
if (var8005dd18 == 0) {
if (g_MenuData.screenshottimer == 1) {
menugfxCreateBlur();
g_MenuData.screenshottimer = 0;
}
if (g_MenuData.screenshottimer >= 2) {
g_MenuData.screenshottimer--;
}
}
t = sc->curRDPTask;
sc->curRDPTask = NULL;
t->state &= ~OS_SC_NEEDS_RDP;
if ((t->state & OS_SC_RCP_MASK) == 0) {
__scTaskComplete(sc, t);
}
__scTryDispatch(sc);
}
static void __scMain(void *arg)
{
void (*msg)(OSSched *sc);
OSSched *sc = (OSSched *)arg;
schedInitArtifacts();
while (1) {
osRecvMesg(&sc->interruptQ, (OSMesg *) &msg, OS_MESG_BLOCK);
msg(sc);
}
}
//-----------------------------------------------------------------------------\
//-- Public functions ---------------------------------------------------------/
//----------------------------------------------------------------------------/
void schedSubmitAudTask(OSSched *sc, OSScTask *t)
{
OSPri prevpri = osGetThreadPri(0);
osSetThreadPri(0, THREADPRI_SCHED + 1);
if (sc->curRSPTask == NULL) {
__scExec(sc, t);
} else {
t->state = OS_SC_NEEDS_RSP;
sc->nextAudTask = t;
}
osSetThreadPri(0, prevpri);
}
void schedSubmitGfxTask(OSSched *sc, OSScTask *t)
{
OSPri prevpri = osGetThreadPri(0);
osSetThreadPri(0, THREADPRI_SCHED + 1);
t->state = OS_SC_NEEDS_RSP | OS_SC_NEEDS_RDP;
if (sc->curRSPTask == NULL && sc->curRDPTask == NULL && sc->queuedFB == NULL) {
g_ScBottleneck = 'C';
__scExec(sc, t);
} else {
g_ScBottleneck = sc->queuedFB ? 'V' : 'R';
if (sc->nextGfxTask == NULL) {
sc->nextGfxTask = t;
} else {
sc->nextGfxTask2 = t;
}
}
osSetThreadPri(0, prevpri);
}
void osScAddClient(OSSched *sc, OSScClient *c, OSMesgQueue *msgQ, int is8mb)
{
OSIntMask mask;
mask = osSetIntMask(1);
if (is8mb) {
sc->audmq = msgQ;
} else {
sc->gfxmq = msgQ;
}
osSetIntMask(mask);
}
void osCreateScheduler(OSSched *sc, OSThread *thread, u8 mode, u32 numFields)
{
sc->audmq = NULL;
sc->gfxmq = NULL;
sc->curRSPTask = NULL;
sc->curRDPTask = NULL;
sc->alt = 0;
sc->nextAudTask = NULL;
sc->nextGfxTask = NULL;
sc->nextGfxTask2 = NULL;
sc->retraceMsg.type = OS_SC_RETRACE_MSG;
sc->prenmiMsg.type = OS_SC_PRE_NMI_MSG;
sc->thread = thread;
sc->scheduledFB = NULL;
sc->queuedFB = NULL;
resetThreadCreate();
osCreateMesgQueue(&sc->interruptQ, sc->intBuf, OS_SC_MAX_MESGS);
osCreateViManager(OS_PRIORITY_VIMGR);
var8008de08 = osViModeTable[mode].comRegs.hStart;
var8008de0c = osViModeTable[mode].fldRegs[0].vStart;
var8008de10 = osViModeTable[mode].fldRegs[1].vStart;
var8008dd60[0] = &var8008dd68[0];
var8008dd60[1] = &var8008dd68[1];
var8008dd68[0] = osViModeTable[mode];
var8008dd68[1] = osViModeTable[mode];
osSetEventMesg(OS_EVENT_SP, &sc->interruptQ, (OSMesg) &__scHandleRSP);
osSetEventMesg(OS_EVENT_DP, &sc->interruptQ, (OSMesg) &__scHandleRDP);
osViSetEvent(&sc->interruptQ, (OSMesg) &__scHandleRetrace, numFields);
osCreateThread(sc->thread, THREAD_SCHED, &__scMain, sc, bootAllocateStack(THREAD_SCHED, STACKSIZE_SCHED), THREADPRI_SCHED);
osStartThread(sc->thread);
}