mm/src/code/graph.c

390 lines
12 KiB
C

#include "ultra64.h"
#include "scheduler.h"
// Variables are put before most headers as a hacky way to bypass bss reordering
struct FaultAddrConvClient sGraphFaultAddrConvClient;
struct FaultClient sGraphFaultClient;
struct GfxMasterList* gGfxMasterDL;
CfbInfo sGraphCfbInfos[3];
OSTime sGraphPrevUpdateEndTime;
#include "regs.h"
#include "fault.h"
#include "macros.h"
#include "buffers.h"
#include "idle.h"
#include "sys_cfb.h"
#include "sys_ucode.h"
#include "libc64/malloc.h"
#include "z64DLF.h"
#include "z64speed_meter.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] = (uintptr_t)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] = (uintptr_t)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);
// Generates code to match gameStateInit to a gamestate entry and returns it if found
#define DEFINE_GAMESTATE_INTERNAL(typeName, enumName) \
if (gameStateInit == typeName##_Init) { \
return &gGameStateOverlayTable[enumName]; \
}
#define DEFINE_GAMESTATE(typeName, enumName, name) DEFINE_GAMESTATE_INTERNAL(typeName, enumName)
#include "tables/gamestate_table.h"
#undef DEFINE_GAMESTATE
#undef DEFINE_GAMESTATE_INTERNAL
return NULL;
}
uintptr_t Graph_FaultAddrConv(uintptr_t address, void* param) {
uintptr_t addr = address;
GameStateOverlay* gameStateOvl = &gGameStateOverlayTable[0];
uintptr_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 sRetryCount = 10;
static s32 sCfbIndex = 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 (sRetryCount >= 0) {
sRetryCount--;
Sched_SendGfxCancelMsg(&gScheduler);
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->ucode_boot = SysUcode_GetUCodeBoot();
task->ucode_boot_size = SysUcode_GetUCodeBootSize();
task->ucode = SysUcode_GetUCode();
task->ucode_data = SysUcode_GetUCodeData();
task->ucode_size = SP_UCODE_SIZE;
task->ucode_data_size = SP_UCODE_DATA_SIZE;
task->dram_stack = (u64*)gGfxSPTaskStack;
task->dram_stack_size = sizeof(gGfxSPTaskStack);
task->output_buff = gGfxSPTaskOutputBufferPtr;
task->output_buff_size = (void*)gGfxSPTaskOutputBufferEnd;
task->data_ptr = (u64*)gGfxMasterDL;
task->data_size = 0;
task->yield_data_ptr = (u64*)gGfxSPTaskYieldBuffer;
task->yield_data_size = 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[sCfbIndex];
sCfbIndex = (sCfbIndex + 1) % ARRAY_COUNT(sGraphCfbInfos);
cfb->framebuffer = gfxCtx->curFrameBuffer;
cfb->swapBuffer = gfxCtx->curFrameBuffer;
if (gfxCtx->updateViMode) {
gfxCtx->updateViMode = false;
cfb->viMode = gfxCtx->viMode;
cfb->viFeatures = 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 = &gScheduler.cmdQueue;
osSendMesg(&gScheduler.cmdQueue, (OSMesg)scTask, OS_MESG_BLOCK);
Sched_SendNotifyMsg(&gScheduler);
}
void Graph_UpdateGame(GameState* gameState) {
GameState_GetInput(gameState);
GameState_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);
GameState_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();
gRSPGfxTimeTotal = gRSPGfxTimeAcc;
gRSPAudioTimeTotal = gRSPAudioTimeAcc;
gRDPTimeTotal = gRDPTimeAcc;
gRSPGfxTimeAcc = 0;
gRSPAudioTimeAcc = 0;
gRDPTimeAcc = 0;
if (sGraphPrevUpdateEndTime != 0) {
gGraphUpdatePeriod = time - sGraphPrevUpdateEndTime;
}
sGraphPrevUpdateEndTime = 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 = malloc(sizeof(*gZBufferLoRes) + sizeof(*gWorkBufferLoRes) + 64 - 1);
gZBufferLoRes = (void*)ALIGN64((u32)gZBufferLoRes);
gWorkBufferLoRes = (void*)((u8*)gZBufferLoRes + sizeof(*gZBufferLoRes));
gGfxSPTaskOutputBufferHiRes = gGfxSPTaskOutputBufferLoRes = 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->file.vromStart);
gameState = 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);
free(gameState);
Overlay_FreeGameState(ovl);
}
Graph_Destroy(&gfxCtx);
}