Fix POLY_XLU_DISP overflow by making gfx buffers 10x larger, added error reporting when gfx buffers overflow

This commit is contained in:
Mr-Wiseguy 2024-02-24 16:13:39 -05:00
parent e97242af2e
commit 5a1dbb96f4
4 changed files with 169 additions and 73 deletions

View File

@ -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();

View File

@ -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
}

View File

@ -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

View File

@ -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