From 5a1dbb96f4098720c793424e2f420dca04bf40a2 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 24 Feb 2024 16:13:39 -0500 Subject: [PATCH] Fix POLY_XLU_DISP overflow by making gfx buffers 10x larger, added error reporting when gfx buffers overflow --- patches/actor_transform_tagging.c | 70 +++----------- patches/patches.ld | 2 +- patches/transform_ids.h | 18 ++++ patches/ui_patches.c | 152 ++++++++++++++++++++++++++---- 4 files changed, 169 insertions(+), 73 deletions(-) diff --git a/patches/actor_transform_tagging.c b/patches/actor_transform_tagging.c index 0df1831..e2eefc4 100644 --- a/patches/actor_transform_tagging.c +++ b/patches/actor_transform_tagging.c @@ -2,18 +2,7 @@ #include "fault.h" #include "transform_ids.h" -// Start after G_EX_ID_IGNORE to avoid assigning it to a group. -u32 next_actor_transform = ACTOR_TRANSFORM_ID_START; - -// Use 32 bits of compiler-inserted padding to hold the actor's transform ID. -// 0x22 between halfDaysBits and world -#define actorIdByte0(actor) ((u8*)(&(actor)->halfDaysBits))[2] -// 0x23 between halfDaysBits and world -#define actorIdByte1(actor) ((u8*)(&(actor)->halfDaysBits))[3] -// 0x3A between audioFlags and focus -#define actorIdByte2(actor) ((u8*)(&(actor)->audioFlags))[1] -// 0x3B between audioFlags and focus -#define actorIdByte3(actor) ((u8*)(&(actor)->audioFlags))[2] +u16 next_actor_transform = 0; extern FaultClient sActorFaultClient; Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play); @@ -45,7 +34,7 @@ void Actor_CleanupContext(ActorContext* actorCtx, PlayState* play) { } // @recomp Reset the actor transform IDs as all actors have been deleted. - next_actor_transform = ACTOR_TRANSFORM_ID_START; + next_actor_transform = 0; Play_SaveCycleSceneFlags(&play->state); ActorOverlayTable_Cleanup(); @@ -53,20 +42,7 @@ void Actor_CleanupContext(ActorContext* actorCtx, PlayState* play) { u32 create_actor_transform_id() { u32 ret = next_actor_transform; - next_actor_transform += ACTOR_TRANSFORM_ID_COUNT; - - // If the actor transform ID has overflowed, wrap back to the starting ID. - if (next_actor_transform < ACTOR_TRANSFORM_ID_START) { - next_actor_transform = ACTOR_TRANSFORM_ID_START; - // Pick a new ID to make sure the actor has a valid range of IDs to use. - ret = next_actor_transform; - next_actor_transform += ACTOR_TRANSFORM_ID_COUNT; - } - - // Skip ranges that include the special transform IDs. - while (G_EX_ID_IGNORE - next_actor_transform < ACTOR_TRANSFORM_ID_COUNT || G_EX_ID_AUTO - next_actor_transform < ACTOR_TRANSFORM_ID_COUNT) { - next_actor_transform += ACTOR_TRANSFORM_ID_COUNT; - } + next_actor_transform++; return ret; } @@ -100,19 +76,13 @@ void Actor_Init(Actor* actor, PlayState* play) { // @recomp Pick a transform ID for this actor and encode it into struct padding u32 cur_transform_id = create_actor_transform_id(); actorIdByte0(actor) = (cur_transform_id >> 0) & 0xFF; - actorIdByte1(actor) = (cur_transform_id >> 8) & 0xFF; - actorIdByte2(actor) = (cur_transform_id >> 16) & 0xFF; - actorIdByte3(actor) = (cur_transform_id >> 24) & 0xFF; + actorIdByte1(actor) = (cur_transform_id >> 8) & 0xFF;; } // Extract the transform ID for this actor, add the limb index and write that as the matrix group to POLY_OPA_DISP. Gfx* push_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { if (actor != NULL) { - u32 cur_transform_id = - (actorIdByte0(actor) << 0) | - (actorIdByte1(actor) << 8) | - (actorIdByte2(actor) << 16) | - (actorIdByte3(actor) << 24); + u32 cur_transform_id = actor_transform_id(actor); gEXMatrixGroup(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_ORDER_LINEAR); } return dlist; @@ -121,11 +91,7 @@ Gfx* push_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { // Extract the transform ID for this actor, add the limb index and post-limb offset and write that as the matrix group to POLY_OPA_DISP. Gfx* push_post_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { if (actor != NULL) { - u32 cur_transform_id = - (actorIdByte0(actor) << 0) | - (actorIdByte1(actor) << 8) | - (actorIdByte2(actor) << 16) | - (actorIdByte3(actor) << 24); + u32 cur_transform_id = actor_transform_id(actor); gEXMatrixGroup(dlist++, cur_transform_id + limb_index + ACTOR_TRANSFORM_LIMB_COUNT, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_ORDER_LINEAR); } return dlist; @@ -134,11 +100,7 @@ Gfx* push_post_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { // Extract the transform ID for this actor, add the limb index and write that as the matrix group to POLY_OPA_DISP. Gfx* push_skin_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) { if (actor != NULL) { - u32 cur_transform_id = - (actorIdByte0(actor) << 0) | - (actorIdByte1(actor) << 8) | - (actorIdByte2(actor) << 16) | - (actorIdByte3(actor) << 24); + u32 cur_transform_id = actor_transform_id(actor); gEXMatrixGroup(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR); } return dlist; @@ -1193,11 +1155,7 @@ void tag_actor_displaylists(Actor* actor, PlayState* play, Gfx* opa_start, Gfx* // If the actor wrote at least one matrix in total and at most one matrix to each list, tag that matrix with the actor's id. if ((opa_matrices == 1 || xlu_matrices == 1) && opa_matrices <= 1 && xlu_matrices <= 1) { - u32 cur_transform_id = - (actorIdByte0(actor) << 0) | - (actorIdByte1(actor) << 8) | - (actorIdByte2(actor) << 16) | - (actorIdByte3(actor) << 24); + u32 cur_transform_id = actor_transform_id(actor); if (opa_matrices == 1) { // Fill in the slot that was reserved for a transform id. @@ -1207,13 +1165,13 @@ void tag_actor_displaylists(Actor* actor, PlayState* play, Gfx* opa_start, Gfx* gEXPopMatrixGroup(POLY_OPA_DISP++); } - // if (xlu_matrices == 1) { - // // Fill in the slot that was reserved for a transform id. - // gEXMatrixGroup(xlu_start, cur_transform_id + 1, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_ORDER_LINEAR); + if (xlu_matrices == 1) { + // Fill in the slot that was reserved for a transform id. + gEXMatrixGroup(xlu_start, cur_transform_id + 1, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_ORDER_LINEAR); - // // Pop the matrix groups. - // gEXPopMatrixGroup(POLY_XLU_DISP++); - // } + // Pop the matrix groups. + gEXPopMatrixGroup(POLY_XLU_DISP++); + } } CLOSE_DISPS(); diff --git a/patches/patches.ld b/patches/patches.ld index d92992a..815af70 100644 --- a/patches/patches.ld +++ b/patches/patches.ld @@ -1,7 +1,7 @@ RAMBASE = 0x80800100; /* Used to hold any new symbols */ MEMORY { - extram : ORIGIN = RAMBASE, LENGTH = 1M + extram : ORIGIN = RAMBASE, LENGTH = 8M rom : ORIGIN = 0, LENGTH = 1M } diff --git a/patches/transform_ids.h b/patches/transform_ids.h index 13d0ede..f0cd97e 100644 --- a/patches/transform_ids.h +++ b/patches/transform_ids.h @@ -14,4 +14,22 @@ #define ACTOR_TRANSFORM_ID_COUNT (ACTOR_TRANSFORM_LIMB_COUNT * 2) // One ID for each limb and another for each post-draw #define ACTOR_TRANSFORM_ID_START 0x1000000U +// Use 16 bits of compiler-inserted padding to hold the actor's transform ID. +// 0x22 between halfDaysBits and world +#define actorIdByte0(actor) ((u8*)(actor))[0x22] +// 0x23 between halfDaysBits and world +#define actorIdByte1(actor) ((u8*)(actor))[0x23] + +// Other unused padding: +// 0x3A between audioFlags and focus +// 0x3B between audioFlags and focus + +static inline u32 actor_transform_id(Actor* actor) { + u32 actor_id = + (actorIdByte0(actor) << 0) | + (actorIdByte1(actor) << 8); + + return (actor_id * ACTOR_TRANSFORM_ID_COUNT) + ACTOR_TRANSFORM_ID_START; +} + #endif diff --git a/patches/ui_patches.c b/patches/ui_patches.c index ad7b49d..6b322d7 100644 --- a/patches/ui_patches.c +++ b/patches/ui_patches.c @@ -8,9 +8,22 @@ s32 margin_reduction = 8; extern s32 gFramerateDivisor; -// Modified to enable RT64 extended GBI mode +// 10 times bigger than the game's normal buffers. +typedef struct { + GfxMasterList master; + Gfx polyXluBuffer[0x8000]; + Gfx overlayBuffer[0x4000]; + Gfx workBuffer[0x400]; + Gfx debugBuffer[0x400]; + Gfx polyOpaBuffer[0x33800]; +} BiggerGfxPool; + +BiggerGfxPool gBiggerGfxPools[2]; + +// @recomp Use the bigger gfx pools and enable RT64 extended GBI mode. void Graph_SetNextGfxPool(GraphicsContext* gfxCtx) { GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx % 2]; + BiggerGfxPool* bigger_pool = &gBiggerGfxPools[gfxCtx->gfxPoolIdx % 2]; gGfxMasterDL = &pool->master; gSegments[0x0E] = (uintptr_t)gGfxMasterDL; @@ -18,29 +31,29 @@ void Graph_SetNextGfxPool(GraphicsContext* gfxCtx) { 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)); + Graph_InitTHGA(&gfxCtx->polyOpa, bigger_pool->polyOpaBuffer, sizeof(bigger_pool->polyOpaBuffer)); + Graph_InitTHGA(&gfxCtx->polyXlu, bigger_pool->polyXluBuffer, sizeof(bigger_pool->polyXluBuffer)); + Graph_InitTHGA(&gfxCtx->overlay, bigger_pool->overlayBuffer, sizeof(bigger_pool->overlayBuffer)); + Graph_InitTHGA(&gfxCtx->work, bigger_pool->workBuffer, sizeof(bigger_pool->workBuffer)); + Graph_InitTHGA(&gfxCtx->debug, bigger_pool->debugBuffer, sizeof(bigger_pool->debugBuffer)); - gfxCtx->polyOpaBuffer = pool->polyOpaBuffer; - gfxCtx->polyXluBuffer = pool->polyXluBuffer; - gfxCtx->overlayBuffer = pool->overlayBuffer; - gfxCtx->workBuffer = pool->workBuffer; - gfxCtx->debugBuffer = pool->debugBuffer; + gfxCtx->polyOpaBuffer = bigger_pool->polyOpaBuffer; + gfxCtx->polyXluBuffer = bigger_pool->polyXluBuffer; + gfxCtx->overlayBuffer = bigger_pool->overlayBuffer; + gfxCtx->workBuffer = bigger_pool->workBuffer; + gfxCtx->debugBuffer = bigger_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); + gSPBranchList(&gGfxMasterDL->disps[0], bigger_pool->polyOpaBuffer); + gSPBranchList(&gGfxMasterDL->disps[1], bigger_pool->polyXluBuffer); + gSPBranchList(&gGfxMasterDL->disps[2], bigger_pool->overlayBuffer); + gSPBranchList(&gGfxMasterDL->disps[3], bigger_pool->workBuffer); gSPEndDisplayList(&gGfxMasterDL->disps[4]); - gSPBranchList(&gGfxMasterDL->debugDisp[0], pool->debugBuffer); + gSPBranchList(&gGfxMasterDL->debugDisp[0], bigger_pool->debugBuffer); // @recomp Enable RT64 extended GBI mode and set the current framerate OPEN_DISPS(gfxCtx); @@ -50,6 +63,113 @@ void Graph_SetNextGfxPool(GraphicsContext* gfxCtx) { CLOSE_DISPS(gfxCtx); } +void recomp_crash(const char* err) { + recomp_printf("%s\n", err); + // TODO open a message box instead of a hard crash + *(volatile int*)0 = 0; +} + +extern volatile OSTime gRSPGfxTimeTotal; +extern volatile OSTime gRSPGfxTimeAcc; +extern volatile OSTime gRSPAudioTimeTotal; +extern volatile OSTime gRSPAudioTimeAcc; +extern volatile OSTime gRDPTimeTotal; +extern volatile OSTime gRDPTimeAcc; +extern OSTime sGraphPrevUpdateEndTime; +extern volatile OSTime gGraphUpdatePeriod; + +// @recomp Modified to report errors instead of skipping frames. +/** + * 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; + + // @recomp Patch all error conditions to print to console and crash the application. + { + GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx % 2]; + + if (pool->headMagic != GFXPOOL_HEAD_MAGIC) { + recomp_crash("GfxPool headMagic integrity check failed!"); + } + if (pool->tailMagic != GFXPOOL_TAIL_MAGIC) { + recomp_crash("GfxPool tailMagic integrity check failed!"); + } + } + + if (THGA_IsCrash(&gfxCtx->polyOpa)) { + recomp_crash("gfxCtx->polyOpa overflow!"); + } + if (THGA_IsCrash(&gfxCtx->polyXlu)) { + recomp_crash("gfxCtx->polyXlu overflow!"); + } + if (THGA_IsCrash(&gfxCtx->overlay)) { + recomp_crash("gfxCtx->overlay overflow!"); + } + if (THGA_IsCrash(&gfxCtx->work)) { + recomp_crash("gfxCtx->work overflow!"); + } + if (THGA_IsCrash(&gfxCtx->debug)) { + recomp_crash("gfxCtx->debug overflow!"); + } + + 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; + } +} + + typedef enum { /* 0 */ PICTO_BOX_STATE_OFF, // Not using the pictograph /* 1 */ PICTO_BOX_STATE_LENS, // Looking through the lens of the pictograph