mm/src/code/graph.c

396 lines
12 KiB
C

#include "z64.h"
#include "regs.h"
#include "functions.h"
#include "fault.h"
// Variables are put before most headers as a hacky way to bypass bss reordering
FaultAddrConvClient sGraphFaultAddrConvClient;
FaultClient sGraphFaultClient;
GfxMasterList* gGfxMasterDL;
CfbInfo sGraphCfbInfos[3];
OSTime sGraphTaskStartTime;
#include "variables.h"
#include "macros.h"
#include "buffers.h"
#include "idle.h"
#include "sys_cfb.h"
#include "system_malloc.h"
#include "overlays/gamestates/ovl_daytelop/z_daytelop.h"
#include "overlays/gamestates/ovl_file_choose/z_file_select.h"
#include "overlays/gamestates/ovl_opening/z_opening.h"
#include "overlays/gamestates/ovl_select/z_select.h"
#include "overlays/gamestates/ovl_title/z_title.h"
#include "z_title_setup.h"
void Graph_FaultClient(void) {
FaultDrawer_DrawText(30, 100, "ShowFrameBuffer PAGE 0/1");
osViSwapBuffer(SysCfb_GetFramebuffer(0));
osViSetMode(gActiveViMode);
osViSetSpecialFeatures(OS_VI_DITHER_FILTER_ON | OS_VI_GAMMA_OFF);
Fault_WaitForInput();
osViSwapBuffer(SysCfb_GetFramebuffer(1));
osViSetMode(gActiveViMode);
osViSetSpecialFeatures(OS_VI_DITHER_FILTER_ON | OS_VI_GAMMA_OFF);
}
void Graph_InitTHGA(TwoHeadGfxArena* arena, Gfx* buffer, s32 size) {
THGA_Init(arena, buffer, size);
}
void Graph_SetNextGfxPool(GraphicsContext* gfxCtx) {
GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx % 2];
gGfxMasterDL = &pool->master;
gSegments[0x0E] = gGfxMasterDL;
pool->headMagic = GFXPOOL_HEAD_MAGIC;
pool->tailMagic = GFXPOOL_TAIL_MAGIC;
Graph_InitTHGA(&gfxCtx->polyOpa, pool->polyOpaBuffer, sizeof(pool->polyOpaBuffer));
Graph_InitTHGA(&gfxCtx->polyXlu, pool->polyXluBuffer, sizeof(pool->polyXluBuffer));
Graph_InitTHGA(&gfxCtx->overlay, pool->overlayBuffer, sizeof(pool->overlayBuffer));
Graph_InitTHGA(&gfxCtx->work, pool->workBuffer, sizeof(pool->workBuffer));
Graph_InitTHGA(&gfxCtx->debug, pool->debugBuffer, sizeof(pool->debugBuffer));
gfxCtx->polyOpaBuffer = pool->polyOpaBuffer;
gfxCtx->polyXluBuffer = pool->polyXluBuffer;
gfxCtx->overlayBuffer = pool->overlayBuffer;
gfxCtx->workBuffer = pool->workBuffer;
gfxCtx->debugBuffer = pool->debugBuffer;
gfxCtx->curFrameBuffer = SysCfb_GetFramebuffer(gfxCtx->framebufferIndex % 2);
gSegments[0x0F] = gfxCtx->curFrameBuffer;
gfxCtx->zbuffer = SysCfb_GetZBuffer();
gSPBranchList(&gGfxMasterDL->disps[0], pool->polyOpaBuffer);
gSPBranchList(&gGfxMasterDL->disps[1], pool->polyXluBuffer);
gSPBranchList(&gGfxMasterDL->disps[2], pool->overlayBuffer);
gSPBranchList(&gGfxMasterDL->disps[3], pool->workBuffer);
gSPEndDisplayList(&gGfxMasterDL->disps[4]);
gSPBranchList(&gGfxMasterDL->debugDisp[0], pool->debugBuffer);
}
GameStateOverlay* Graph_GetNextGameState(GameState* gameState) {
GameStateFunc gameStateInit = GameState_GetInit(gameState);
if (gameStateInit == Setup_Init) {
return &gGameStateOverlayTable[0];
}
if (gameStateInit == MapSelect_Init) {
return &gGameStateOverlayTable[1];
}
if (gameStateInit == ConsoleLogo_Init) {
return &gGameStateOverlayTable[2];
}
if (gameStateInit == Play_Init) {
return &gGameStateOverlayTable[3];
}
if (gameStateInit == TitleSetup_Init) {
return &gGameStateOverlayTable[4];
}
if (gameStateInit == FileSelect_Init) {
return &gGameStateOverlayTable[5];
}
if (gameStateInit == DayTelop_Init) {
return &gGameStateOverlayTable[6];
}
return NULL;
}
uintptr_t Graph_FaultAddrConv(uintptr_t address, void* param) {
uintptr_t addr = address;
GameStateOverlay* gameStateOvl = &gGameStateOverlayTable[0];
size_t ramConv;
void* ramStart;
size_t diff;
s32 i;
for (i = 0; i < gGraphNumGameStates; i++, gameStateOvl++) {
diff = (uintptr_t)gameStateOvl->vramEnd - (uintptr_t)gameStateOvl->vramStart;
ramStart = gameStateOvl->loadedRamAddr;
ramConv = (uintptr_t)gameStateOvl->vramStart - (uintptr_t)ramStart;
if (ramStart != NULL) {
if ((addr >= (uintptr_t)ramStart) && (addr < (uintptr_t)ramStart + diff)) {
return addr + ramConv;
}
}
}
return 0;
}
void Graph_Init(GraphicsContext* gfxCtx) {
bzero(gfxCtx, sizeof(GraphicsContext));
gfxCtx->gfxPoolIdx = 0;
gfxCtx->framebufferIndex = 0;
gfxCtx->viMode = NULL;
gfxCtx->viConfigFeatures = gViConfigFeatures;
gfxCtx->xScale = gViConfigXScale;
gfxCtx->yScale = gViConfigYScale;
osCreateMesgQueue(&gfxCtx->queue, gfxCtx->msgBuff, ARRAY_COUNT(gfxCtx->msgBuff));
Fault_AddClient(&sGraphFaultClient, (void*)Graph_FaultClient, NULL, NULL);
Fault_AddAddrConvClient(&sGraphFaultAddrConvClient, Graph_FaultAddrConv, NULL);
}
void Graph_Destroy(GraphicsContext* gfxCtx) {
Fault_RemoveClient(&sGraphFaultClient);
Fault_RemoveAddrConvClient(&sGraphFaultAddrConvClient);
}
/**
* Constructs the graphics OSTask and forwards it to the scheduler.
* Waits for up to 3 additional seconds for any current graphics task to complete.
* If it does not signal completion in that time, retry or trigger a crash.
*/
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;
retry:
osSetTimer(&timer, OS_USEC_TO_CYCLES(3 * 1000 * 1000), 0, &gfxCtx->queue, (OSMesg)666);
osRecvMesg(&gfxCtx->queue, &msg, OS_MESG_BLOCK);
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);
}
gfxCtx->schedMsgQ = &gSchedContext.cmdQ;
osSendMesg(&gSchedContext.cmdQ, scTask, OS_MESG_BLOCK);
Sched_SendEntryMsg(&gSchedContext);
}
void Graph_UpdateGame(GameState* gameState) {
Game_UpdateInput(gameState);
Game_IncrementFrameCount(gameState);
if (SREG(20) < 3) {
Audio_Update();
}
}
/**
* Run the game state logic, then finalize the gfx buffer
* and run the graphics task for this frame.
*/
void Graph_ExecuteAndDraw(GraphicsContext* gfxCtx, GameState* gameState) {
u32 problem;
gameState->unk_A3 = 0;
Graph_SetNextGfxPool(gfxCtx);
Game_Update(gameState);
OPEN_DISPS(gfxCtx);
gSPEndDisplayList(WORK_DISP++);
gSPEndDisplayList(POLY_OPA_DISP++);
gSPEndDisplayList(POLY_XLU_DISP++);
gSPEndDisplayList(OVERLAY_DISP++);
gSPEndDisplayList(DEBUG_DISP++);
CLOSE_DISPS(gfxCtx);
{
Gfx* gfx = gGfxMasterDL->taskStart;
gSPSegment(gfx++, 0x0E, gGfxMasterDL);
gSPDisplayList(gfx++, &D_0E000000.disps[3]);
gSPDisplayList(gfx++, &D_0E000000.disps[0]);
gSPDisplayList(gfx++, &D_0E000000.disps[1]);
gSPDisplayList(gfx++, &D_0E000000.disps[2]);
gSPDisplayList(gfx++, &D_0E000000.debugDisp[0]);
gDPPipeSync(gfx++);
gDPFullSync(gfx++);
gSPEndDisplayList(gfx++);
}
problem = false;
{
GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx % 2];
if (pool->headMagic != GFXPOOL_HEAD_MAGIC) {
Fault_AddHungupAndCrash("../graph.c", 1054);
}
if (pool->tailMagic != GFXPOOL_TAIL_MAGIC) {
Fault_AddHungupAndCrash("../graph.c", 1066);
}
}
if (THGA_IsCrash(&gfxCtx->polyOpa)) {
problem = true;
}
if (THGA_IsCrash(&gfxCtx->polyXlu)) {
problem = true;
}
if (THGA_IsCrash(&gfxCtx->overlay)) {
problem = true;
}
if (THGA_IsCrash(&gfxCtx->work)) {
problem = true;
}
if (THGA_IsCrash(&gfxCtx->debug)) {
problem = true;
}
if (!problem) {
Graph_TaskSet00(gfxCtx, gameState);
gfxCtx->gfxPoolIdx++;
gfxCtx->framebufferIndex++;
}
{
OSTime time = osGetTime();
D_801FBAE8 = sRSPGFXTotalTime;
D_801FBAE0 = gRSPAudioTotalTime;
D_801FBAF0 = gRDPTotalTime;
sRSPGFXTotalTime = 0;
gRSPAudioTotalTime = 0;
gRDPTotalTime = 0;
if (sGraphTaskStartTime != 0) {
lastRenderFrameDuration = time - sGraphTaskStartTime;
}
sGraphTaskStartTime = time;
}
}
void Graph_Update(GraphicsContext* gfxCtx, GameState* gameState) {
gameState->unk_A3 = 0;
Graph_UpdateGame(gameState);
Graph_ExecuteAndDraw(gfxCtx, gameState);
}
void Graph_ThreadEntry(void* arg) {
GraphicsContext gfxCtx;
GameStateOverlay* nextOvl = &gGameStateOverlayTable[0];
GameStateOverlay* ovl;
GameState* gameState;
u32 size;
s32 pad[2];
gZBufferLoRes = SystemArena_Malloc(sizeof(*gZBufferLoRes) + sizeof(*gWorkBufferLoRes) + 64 - 1);
gZBufferLoRes = (void*)ALIGN64((u32)gZBufferLoRes);
gWorkBufferLoRes = (void*)((u8*)gZBufferLoRes + sizeof(*gZBufferLoRes));
gGfxSPTaskOutputBufferHiRes = gGfxSPTaskOutputBufferLoRes =
SystemArena_Malloc(sizeof(*gGfxSPTaskOutputBufferLoRes));
gGfxSPTaskOutputBufferEndLoRes = (u8*)gGfxSPTaskOutputBufferLoRes + sizeof(*gGfxSPTaskOutputBufferLoRes);
gGfxSPTaskOutputBufferEndHiRes = (u8*)gGfxSPTaskOutputBufferHiRes + sizeof(*gGfxSPTaskOutputBufferHiRes);
SysCfb_Init();
Fault_SetFrameBuffer(gWorkBuffer, SCREEN_WIDTH, SCREEN_HEIGHT);
Graph_Init(&gfxCtx);
while (nextOvl) {
ovl = nextOvl;
Overlay_LoadGameState(ovl);
size = ovl->instanceSize;
func_800809F4(ovl->vromStart);
gameState = SystemArena_Malloc(size);
bzero(gameState, size);
GameState_Init(gameState, ovl->init, &gfxCtx);
while (GameState_IsRunning(gameState)) {
Graph_Update(&gfxCtx, gameState);
}
nextOvl = Graph_GetNextGameState(gameState);
if (size) {}
GameState_Destroy(gameState);
SystemArena_Free(gameState);
Overlay_FreeGameState(ovl);
}
Graph_Destroy(&gfxCtx);
}