#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); }