From 45509382e7a868211d305df040598bd4e7a38339 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 14:04:01 -0700 Subject: [PATCH 01/79] psdisp: take Ford Lascari's psDispParticles improvements From the open PR #3223: an operand-order temp in psDispSub's axis.y, the appsrt mmtx argument spelled in place, an explicit transformed_x carrier and prev_pos.y operand swap in psDispSubAPPSRTPoint, and the psDispParticles declaration set renamed and reordered. 98.6764% -> 98.7148%. Co-authored-by: Ford Lascari --- src/sysdolphin/baselib/psdisp.c | 90 +++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index c837fe02cb..8c02962698 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1159,7 +1159,10 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) f32 uz = up_z; axis.x = ry * uz - rz * up_y; - axis.y = rz * ux - rx * uz; + { + f32 axis_y_product = ux * rz; + axis.y = axis_y_product - rx * uz; + } axis.z = rx * up_y - ry * ux; PSMTXRotAxisRad(mtx, &axis, angle); t1 = mtx[1][0] * rx + mtx[1][1] * ry; @@ -1209,9 +1212,8 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) Vec3* translate = &appsrt->translate; Vec3* rotate = (Vec3*) &appsrt->rot; Vec3* scale = &appsrt->scale; - MtxPtr mmtx = appsrt->mmtx; - HSD_MtxSRT(mmtx, scale, rotate, translate, NULL); + HSD_MtxSRT(appsrt->mmtx, scale, rotate, translate, NULL); } if (pp->appsrt->status == PS_APPSTATUS_ONCE) { pp->appsrt->status = PS_APPSTATUS_STILL; @@ -1249,6 +1251,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) { HSD_psAppSRT* appsrt = pp->appsrt; f32 pos_y; + f32 transformed_x; cur_pos.z = appsrt->x88 * (pos_y = pp->pos.y); cur_pos.y = appsrt->x78 * pos_y; @@ -1256,7 +1259,8 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) cur_pos.y = appsrt->x74 * pp->pos.x + cur_pos.y; cur_pos.z = (cur_pos.x = appsrt->ssx * pp->pos.x + cur_pos.x, appsrt->x84 * pp->pos.x + cur_pos.z); - cur_pos.x = appsrt->x6C * pp->pos.z + cur_pos.x; + transformed_x = appsrt->x6C * pp->pos.z + cur_pos.x; + cur_pos.x = transformed_x; cur_pos.y = appsrt->x7C * pp->pos.z + cur_pos.y; cur_pos.z = appsrt->x8C * pp->pos.z + cur_pos.z; cur_pos.x = appsrt->x70 + cur_pos.x; @@ -1287,7 +1291,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) (pp->appsrt->ssx * dx + pp->appsrt->ssy * dy)); prev_pos.y = pp->appsrt->x80 + (pp->appsrt->x7C * dz + - (pp->appsrt->x74 * dx + pp->appsrt->x78 * dy)); + (pp->appsrt->x78 * dy + pp->appsrt->x74 * dx)); prev_pos.z = pp->appsrt->x90 + (pp->appsrt->x8C * dz + (pp->appsrt->x84 * dx + pp->appsrt->x88 * dy)); @@ -1937,25 +1941,25 @@ static inline void psUpdateProjectionCache(f32 perspective) #endif void psDispParticles(u32 target_link, u32 sw) { - s32 var_r16; - s32 var_r15; + GXColor chan_mat_color; + s32 needs_setup; s32 sp7B4; void* sp7B0; u32 sp7AC; u32 sp7A8; u8 sp7A5; u8 sp7A4; - s32 sp7A0; + GXColor chan_amb_color; void* sp79C; psdisp_Tlut tlut_obj; GXTexObj sp764; - HSD_Particle* sp760; - HSD_Particle* sp75C; + HSD_Particle* sorted_particles; + HSD_Particle* non_edge_particles; psdisp_Mtx billboard_mtx; - GXTlutObj sp71C; f32 sp700; - GXColor sp6E0; - GXColor sp6DC; + GXTlutObj gx_tlut_obj; + s32 alpha_compare_mode; + s32 prev_tex_interp_near; GXColor sp6D8; GXColor sp6D4; GXColor sp6D0; @@ -1964,11 +1968,11 @@ void psDispParticles(u32 target_link, u32 sw) /// @todo Recover this stack space from the original inline hierarchy. PAD_STACK(0x14); - var_r16 = 0; - var_r15 = 0; + alpha_compare_mode = 0; + prev_tex_interp_near = 0; sp7A5 = 0; sp7A4 = 0xFF; - sp7A0 = 1; + needs_setup = 1; if (sw == 0) { if (psFrameNum < 0xFFU) { psFrameNum += 1; @@ -1980,11 +1984,12 @@ void psDispParticles(u32 target_link, u32 sw) sp7B4 = 0; do { if (target_link & (1 << sp7B4)) { - particleSort(sp7B4, psFrameNum, &sp760, &sp75C); + particleSort(sp7B4, psFrameNum, &sorted_particles, + &non_edge_particles); if (sw == 1) { - pp = sp760; + pp = sorted_particles; } else { - pp = sp75C; + pp = non_edge_particles; } while (pp != NULL) { HSD_PSTexGroup* tex_group = NULL; @@ -2009,7 +2014,7 @@ void psDispParticles(u32 target_link, u32 sw) break; } if (!(pp->size < FLT_EPSILON)) { - if (sp7A0 != 0) { + if (needs_setup != 0) { sp79C = NULL; prevPointSize = -1; sp7B0 = NULL; @@ -2023,10 +2028,10 @@ void psDispParticles(u32 target_link, u32 sw) prevChanMat.r = prevChanMat.g = prevChanMat.b = 0xFF; prevChanAmb.r = prevChanAmb.g = prevChanAmb.b = 0xFF; prevChanMat.a = prevChanAmb.a = 0xFF; - sp6E0 = prevChanMat; - GXSetChanMatColor(GX_COLOR0A0, sp6E0); - sp6DC = prevChanAmb; - GXSetChanAmbColor(GX_COLOR0A0, sp6DC); + chan_mat_color = prevChanMat; + GXSetChanMatColor(GX_COLOR0A0, chan_mat_color); + chan_amb_color = prevChanAmb; + GXSetChanAmbColor(GX_COLOR0A0, chan_amb_color); psSetupTevInvalidState(); psSetupTevCommon(); psSetColor(&prevColorPrim, 0xFF); @@ -2057,7 +2062,7 @@ void psDispParticles(u32 target_link, u32 sw) psSetupVtxFormat(GX_VTXFMT3, true, false, GX_RGB565); psSetupVtxFormat(GX_VTXFMT4, false, true, GX_RGBA6); psSetupVtxFormat(GX_VTXFMT5, true, true, GX_RGBA6); - sp7A0 = 0; + needs_setup = 0; } blend_mode = (pp->kind >> 0x16U) & 3; @@ -2077,15 +2082,15 @@ void psDispParticles(u32 target_link, u32 sw) alpha0 = pp->aCmpParam1; alpha1 = pp->aCmpParam2; } - if ((var_r16 != pp->aCmpMode) || (sp7A5 != alpha0) || - (sp7A4 != alpha1)) + if ((alpha_compare_mode != pp->aCmpMode) || + (sp7A5 != alpha0) || (sp7A4 != alpha1)) { sp7A5 = alpha0; - var_r16 = pp->aCmpMode; + alpha_compare_mode = pp->aCmpMode; sp7A4 = alpha1; - GXSetAlphaCompare((var_r16 >> 3) & 7, sp7A5, - (var_r16 >> 6) & 3, var_r16 & 7, - sp7A4); + GXSetAlphaCompare((alpha_compare_mode >> 3) & 7, sp7A5, + (alpha_compare_mode >> 6) & 3, + alpha_compare_mode & 7, sp7A4); } psSetupTev((u32*) pp); @@ -2187,10 +2192,11 @@ void psDispParticles(u32 target_link, u32 sw) tlut_obj.tlut_name = GX_TLUT0; tlut_obj.n_entries = (fmt == GX_TF_C4) ? 0x10 : 0x100; - GXInitTlutObj(&sp71C, tlut, + GXInitTlutObj(&gx_tlut_obj, tlut, tlut_obj.fmt, tlut_obj.n_entries); - GXLoadTlut(&sp71C, tlut_obj.tlut_name); + GXLoadTlut(&gx_tlut_obj, + tlut_obj.tlut_name); sp7B0 = NULL; } } @@ -2220,19 +2226,25 @@ void psDispParticles(u32 target_link, u32 sw) HSD_ASSERT(0x8AA, 0); break; } - var_r15 = pp->kind & TexInterpNear; + prev_tex_interp_near = pp->kind & TexInterpNear; GXInitTexObjLOD( - &sp764, (var_r15 != 0) ? GX_NEAR : GX_LINEAR, + &sp764, + (prev_tex_interp_near != 0) ? GX_NEAR + : GX_LINEAR, (pp->kind & TexInterpNear) ? GX_NEAR : GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); GXLoadTexObj(&sp764, GX_TEXMAP0); } - if ((u32) var_r15 != (pp->kind & TexInterpNear)) { - var_r15 = pp->kind & TexInterpNear; + if ((u32) prev_tex_interp_near != + (pp->kind & TexInterpNear)) + { + prev_tex_interp_near = pp->kind & TexInterpNear; GXInitTexObjLOD( - &sp764, (var_r15 != 0) ? GX_NEAR : GX_LINEAR, + &sp764, + (prev_tex_interp_near != 0) ? GX_NEAR + : GX_LINEAR, (s32) (pp->kind & TexInterpNear) != 0 ? GX_NEAR : GX_LINEAR, @@ -2265,7 +2277,7 @@ void psDispParticles(u32 target_link, u32 sw) } sp7B4 += 1; } while (sp7B4 < 0x10); - if (sp7A0 == 0) { + if (needs_setup == 0) { HSD_StateInvalidate(-1); } } From 7c8baa257f42373cc76991e55d1c2a11c5220d4e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 14:24:14 -0700 Subject: [PATCH 02/79] psdisp: spell the projection cache from prj[] directly psUpdateProjectionCache's non-perspective branch reads prj[1..4] at each use instead of through staged locals, which recovers the target's load order, and the perspective branch folds the x_offset product into the store expression. 98.7148% -> 98.7341%. --- src/sysdolphin/baselib/psdisp.c | 36 +++++++++++---------------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 8c02962698..bfaf4056ad 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1886,24 +1886,19 @@ static inline void psUpdateProjectionCache(f32 perspective) f32 w3; f32 y_scale; f32 y_offset; - f32 product; f32 y0; f32 y1; f32 y2; f32 y3; w0 = vmtx[2][0]; - product = x_offset * w0; - pvmtx[0][0] = x_scale * vmtx[0][0] + product; + pvmtx[0][0] = x_scale * vmtx[0][0] + x_offset * w0; w1 = vmtx[2][1]; - product = x_offset * w1; - pvmtx[0][1] = x_scale * vmtx[0][1] + product; + pvmtx[0][1] = x_scale * vmtx[0][1] + x_offset * w1; w2 = vmtx[2][2]; - product = x_offset * w2; - pvmtx[0][2] = x_scale * vmtx[0][2] + product; + pvmtx[0][2] = x_scale * vmtx[0][2] + x_offset * w2; w3 = vmtx[2][3]; - product = x_offset * w3; - pvmtx[0][3] = x_scale * vmtx[0][3] + product; + pvmtx[0][3] = x_scale * vmtx[0][3] + x_offset * w3; y_scale = prj[3]; y_offset = prj[4]; y0 = y_offset * w0; @@ -1915,21 +1910,14 @@ static inline void psUpdateProjectionCache(f32 perspective) pvmtx[1][2] = y_scale * vmtx[1][2] + y2; pvmtx[1][3] = y_scale * vmtx[1][3] + y3; } else { - f32 x_offset = prj[2]; - f32 x_scale = prj[1]; - f32 y_offset; - f32 y_scale; - - pvmtx[0][0] = x_scale * vmtx[0][0] + x_offset; - pvmtx[0][1] = x_scale * vmtx[0][1] + x_offset; - pvmtx[0][2] = x_scale * vmtx[0][2] + x_offset; - pvmtx[0][3] = x_scale * vmtx[0][3] + x_offset; - y_scale = prj[3]; - y_offset = prj[4]; - pvmtx[1][0] = y_scale * vmtx[1][0] + y_offset; - pvmtx[1][1] = y_scale * vmtx[1][1] + y_offset; - pvmtx[1][2] = y_scale * vmtx[1][2] + y_offset; - pvmtx[1][3] = y_scale * vmtx[1][3] + y_offset; + pvmtx[0][0] = prj[1] * vmtx[0][0] + prj[2]; + pvmtx[0][1] = prj[1] * vmtx[0][1] + prj[2]; + pvmtx[0][2] = prj[1] * vmtx[0][2] + prj[2]; + pvmtx[0][3] = prj[1] * vmtx[0][3] + prj[2]; + pvmtx[1][0] = prj[3] * vmtx[1][0] + prj[4]; + pvmtx[1][1] = prj[3] * vmtx[1][1] + prj[4]; + pvmtx[1][2] = prj[3] * vmtx[1][2] + prj[4]; + pvmtx[1][3] = prj[3] * vmtx[1][3] + prj[4]; } /// @todo Passing @c rvmtx directly swaps the first two axis loads. psUpdateBillboardAxes(*(const Mtx*) rvmtx); From b7a4e8c144b4a5016f8f48d6dbae19f0b4d96f17 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 14:41:35 -0700 Subject: [PATCH 03/79] psdisp: recover two axis-order stream shapes psDispSub's axis.y stages the subtrahend so the cross product emits fmsubs instead of fnmsubs, and psDispSubAPPSRTPoint's cur_pos evaluates its x, y and z column products in that order. Four real instruction-stream rows recovered; the nominal score moves -0.02% because both changes trade instruction rows for register/displacement rows. --- src/sysdolphin/baselib/psdisp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index bfaf4056ad..dfad62ecb8 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1160,8 +1160,8 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) axis.x = ry * uz - rz * up_y; { - f32 axis_y_product = ux * rz; - axis.y = axis_y_product - rx * uz; + f32 axis_y_product = rx * uz; + axis.y = rz * ux - axis_y_product; } axis.z = rx * up_y - ry * ux; PSMTXRotAxisRad(mtx, &axis, angle); @@ -1253,9 +1253,9 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) f32 pos_y; f32 transformed_x; - cur_pos.z = appsrt->x88 * (pos_y = pp->pos.y); + cur_pos.x = appsrt->ssy * (pos_y = pp->pos.y); cur_pos.y = appsrt->x78 * pos_y; - cur_pos.x = appsrt->ssy * pos_y; + cur_pos.z = appsrt->x88 * pos_y; cur_pos.y = appsrt->x74 * pp->pos.x + cur_pos.y; cur_pos.z = (cur_pos.x = appsrt->ssx * pp->pos.x + cur_pos.x, appsrt->x84 * pp->pos.x + cur_pos.z); From db3d6803e25a8b9feaec962c838067ed21632c66 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 14:54:44 -0700 Subject: [PATCH 04/79] psdisp: evaluate the previous-position deltas y-first Both prev_pos blocks declare dy before dx, which recovers the target's velocity load order in psDispSubAPPSRTPoint and one scheduling row in psDispSubAppSRT. Real instruction-stream divergence is now 23 rows (was 34); the nominal score falls to 98.6314% because the recovered rows trade for insert/delete alignment pairs that objdiff weights more heavily than the argument mismatches they replace. --- src/sysdolphin/baselib/psdisp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index dfad62ecb8..1242248df7 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1283,8 +1283,8 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) pp->appsrt->x90 + (pp->appsrt->x8C * z + (pp->appsrt->x84 * x + pp->appsrt->x88 * y)); } else { - f32 dx = pp->pos.x - pp->vel.x; f32 dy = pp->pos.y - pp->vel.y; + f32 dx = pp->pos.x - pp->vel.x; f32 dz = pp->pos.z - pp->vel.z; prev_pos.x = pp->appsrt->x70 + (pp->appsrt->x6C * dz + @@ -1456,8 +1456,8 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) draw_mtx[2][3] + (draw_mtx[2][2] * z + (draw_mtx[2][0] * x + draw_mtx[2][1] * y)); } else { - f32 dx = pp->pos.x - pp->vel.x; f32 dy = pp->pos.y - pp->vel.y; + f32 dx = pp->pos.x - pp->vel.x; f32 dz = pp->pos.z - pp->vel.z; prev_pos.x = From e4d3e94172bd9238291882394e91165157e28432 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 15:37:54 -0700 Subject: [PATCH 05/79] psdisp: declare the material colour staging below tlut_obj Moving chan_mat_color after tlut_obj puts tlut_obj at the target's 0x788 with the same four-byte hole below it, recovering six frame rows. 827 rows, 456 of them stack displacements (was 833/462). --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 1242248df7..59fd1025a2 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1929,7 +1929,6 @@ static inline void psUpdateProjectionCache(f32 perspective) #endif void psDispParticles(u32 target_link, u32 sw) { - GXColor chan_mat_color; s32 needs_setup; s32 sp7B4; void* sp7B0; @@ -1940,6 +1939,7 @@ void psDispParticles(u32 target_link, u32 sw) GXColor chan_amb_color; void* sp79C; psdisp_Tlut tlut_obj; + GXColor chan_mat_color; GXTexObj sp764; HSD_Particle* sorted_particles; HSD_Particle* non_edge_particles; From 3c6da5dc5bf30803e65457ab554edc831602bd78 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 15:45:33 -0700 Subject: [PATCH 06/79] psdisp: order the APPSRTPoint x-column store first cur_pos.x takes its own statement and the comma expression carries y into z, which recovers the target's ssx/x74/x84 load order. Two more real instruction-stream rows; 46 remain. --- src/sysdolphin/baselib/psdisp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 59fd1025a2..af8f3bc6cc 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1256,8 +1256,8 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) cur_pos.x = appsrt->ssy * (pos_y = pp->pos.y); cur_pos.y = appsrt->x78 * pos_y; cur_pos.z = appsrt->x88 * pos_y; - cur_pos.y = appsrt->x74 * pp->pos.x + cur_pos.y; - cur_pos.z = (cur_pos.x = appsrt->ssx * pp->pos.x + cur_pos.x, + cur_pos.x = appsrt->ssx * pp->pos.x + cur_pos.x; + cur_pos.z = (cur_pos.y = appsrt->x74 * pp->pos.x + cur_pos.y, appsrt->x84 * pp->pos.x + cur_pos.z); transformed_x = appsrt->x6C * pp->pos.z + cur_pos.x; cur_pos.x = transformed_x; From 0ad1bee4fecf4c02bc695275303011de91d6a66e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 16:02:02 -0700 Subject: [PATCH 07/79] psdisp: inline psDispParticles to depth 4 The depth-3 cap left an extra by-value staging slot at the bottom of the frame (our band started at 0x1c, the target's at 0x18) and doubled the inline-site pool below gx_tlut_obj. Raising the cap recovers both; PAD_STACK grows to 0x3c to hold the frame at 0x9a0 while the inline-local objects those bytes stand for are still unreconstructed. Identical instruction stream and register allocation, 12 fewer frame-displacement rows: 815 rows, 444 of them stack. --- src/sysdolphin/baselib/psdisp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index af8f3bc6cc..4a658f82ac 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1925,7 +1925,7 @@ static inline void psUpdateProjectionCache(f32 perspective) #ifdef MUST_MATCH #pragma push -#pragma inline_depth(3) +#pragma inline_depth(4) #endif void psDispParticles(u32 target_link, u32 sw) { @@ -1954,7 +1954,7 @@ void psDispParticles(u32 target_link, u32 sw) u32 prev_kind; HSD_Particle* pp; /// @todo Recover this stack space from the original inline hierarchy. - PAD_STACK(0x14); + PAD_STACK(0x3c); alpha_compare_mode = 0; prev_tex_interp_near = 0; From b3250d1e844ef0d190cc5c6a9c5d49d70d3a2ffd Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 16:15:57 -0700 Subject: [PATCH 08/79] psdisp: declare the trail colour buffer before the vertex buffer Arrays allocate in reverse declaration order, so cbuf first puts it above vbuf as the target has it. 808 rows, 437 stack. --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 4a658f82ac..7eec84110c 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -617,8 +617,8 @@ static inline HSD_Particle* psDispSubPoint(HSD_Particle* pp) static inline HSD_Particle* psDispSubPointTrail(HSD_Particle* pp) { - Vec3 vbuf[32]; GXColor cbuf[32]; + Vec3 vbuf[32]; Vec3* p; GXColor* c; HSD_Particle* last; From 76ca09e3d3918b2cd9294663e0b065e4e8c9da73 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 17:01:00 -0700 Subject: [PATCH 09/79] psdisp: expand psDispSubMakePolygon into its only caller The allocator capture shows the frame is the homed-object list in reverse creation order, and that this helper's temps are created last in ours but early in the target. Splicing its body into psDispSub -- its single call site, since Melee's AppSRT path does not share it -- lowers those objects in psDispSub's own order and shrinks the unaccounted inline pool from 88 bytes to 40, so PAD_STACK drops to 0xC. 98.6389% -> 98.7612% (above master's 98.6764%), 808 -> 770 rows, and register allocation rows fall 325 -> 280. --- src/sysdolphin/baselib/psdisp.c | 440 ++++++++++++++++---------------- 1 file changed, 217 insertions(+), 223 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 7eec84110c..2b54ec8aba 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -805,224 +805,6 @@ static inline void psSetCurrentMtx(GXPosNrmMtx idx) } } -static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, - f32 y, f32 z, f32 ppvx, f32 ppvy, - f32 ppvz, f32 x0, f32 y0, f32 z0, - f32 x1, f32 y1, f32 z1) -{ - f32 prev_x; - f32 prev_y; - f32 prev_z; - u8* it = texform; - - psSetCurrentMtx(GX_PNMTX0); - if (pp->kind & Trail) { - GXColor color; - - if (pp->kind & Tornado) { - calcTornadoLastPos(pp, &prev_x, &prev_y, &prev_z); - } else { - prev_x = x - pp->vel.x; - prev_y = y - pp->vel.y; - prev_z = z - pp->vel.z; - } - getClrTrail(pp, &color); - if (it == NULL) { - if (pp->kind & DispTexture) { - setVtxDesc(2); - GXBegin(GX_QUADS, GX_VTXFMT2, 4); - } else { - setVtxDesc(3); - GXBegin(GX_QUADS, GX_VTXFMT3, 4); - } - GXPosition3f32(prev_x - x0, prev_y - y0, prev_z - z0); - { - u8 a = color.a; - f32 alpha = (f32) a * pp->trail; - u8 b = color.b; - u8 g = color.g; - u8 r = color.r; - GXColor4u8(r, g, b, (u8) alpha); - } - if (pp->kind & DispTexture) { - GXWGFifo.u8 = (pp->kind >> 16) & 0xC; - } - GXPosition3f32(x - x1, y - y1, z - z1); - { - u8 a = color.a; - u8 b = color.b; - u8 g = color.g; - u8 r = color.r; - GXColor4u8(r, g, b, a); - } - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; - } - GXPosition3f32(x + x0, y + y0, z + z0); - { - u8 a = color.a; - u8 b = color.b; - u8 g = color.g; - u8 r = color.r; - GXColor4u8(r, g, b, a); - } - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; - } - GXPosition3f32(prev_x + x1, prev_y + y1, prev_z + z1); - { - u8 a = color.a; - f32 alpha = (f32) a * pp->trail; - u8 b = color.b; - u8 g = color.g; - u8 r = color.r; - GXColor4u8(r, g, b, (u8) alpha); - } - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 3; - } - } else { - f32 trail_alpha = 255.0f * (1.0f - pp->trail); - f32 up_len = sqrtf(x1 * x1 + y1 * y1 + z1 * z1); - - if (up_len != 0.0f) { - f32 dx = x - prev_x; - f32 dy = y - prev_y; - f32 dz = z - prev_z; - f32 xl = dx * dx; - f32 yl = dy * dy; - f32 zl = dz * dz; - f32 segment_len = sqrtf(zl + (xl + yl)); - f32 ratio = segment_len / up_len; - u32 primitive_count = *(u32*) it; - - it += sizeof(u32); - x1 *= ratio; - y1 *= ratio; - z1 *= ratio; - for (; primitive_count != 0; primitive_count--) { - GXPrimitive primitive = it[0]; - u8 count = it[1]; - s32 i; - u8 b; - u8 g; - u8 r; - - it += 4; - if (pp->kind & DispTexture) { - setVtxDesc(5); - GXBegin(primitive, GX_VTXFMT5, count); - } else { - setVtxDesc(3); - GXBegin(primitive, GX_VTXFMT3, count); - } - b = color.b; - g = color.g; - r = color.r; - for (i = count; i > 0; i--) { - f32 s = *(f32*) &it[0]; - f32 sx = 2.0f * (s - 0.5f); - f32 t; - f32 tx; - s32 converted_alpha; - s32 alpha; - - if (pp->kind & TexFlipS) { - s = 1.0f - s; - } - t = *(f32*) &it[4]; - it += 8; - converted_alpha = (s32) (255.0f - t * trail_alpha); - alpha = (s32) (255.0f - t * trail_alpha); - if (converted_alpha < 0) { - alpha = 0; - } - if (alpha > 0xFF) { - alpha = 0xFF; - } - tx = 2.0f * (t - 0.5f); - if (pp->kind & TexFlipT) { - t = 1.0f - t; - } - GXWGFifo.f32 = x1 * tx + (x0 * sx + x); - GXWGFifo.f32 = y1 * tx + (y0 * sx + y); - GXWGFifo.f32 = z1 * tx + (z0 * sx + z); - GXColor4u8(r, g, b, alpha); - if (pp->kind & DispTexture) { - GXWGFifo.f32 = s; - GXWGFifo.f32 = t; - } - } - } - } - } - } else if (it == NULL) { - if (pp->kind & DispTexture) { - setVtxDesc(0); - GXBegin(GX_QUADS, GX_VTXFMT0, 4); - } else { - setVtxDesc(1); - GXBegin(GX_QUADS, GX_VTXFMT1, 4); - } - GXPosition3f32(x - x0, y - y0, z - z0); - if (pp->kind & DispTexture) { - GXWGFifo.u8 = (pp->kind >> 16) & 0xC; - } - GXPosition3f32(x - x1, y - y1, z - z1); - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; - } - GXPosition3f32(x + x0, y + y0, z + z0); - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; - } - GXPosition3f32(x + x1, y + y1, z + z1); - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 3; - } - } else { - u32 primitive_count = *(u32*) it; - it += sizeof(u32); - for (; primitive_count != 0; primitive_count--) { - GXPrimitive primitive = it[0]; - u8 count = it[1]; - s32 i; - - it += 4; - if (pp->kind & DispTexture) { - setVtxDesc(4); - GXBegin(primitive, GX_VTXFMT4, count); - } else { - setVtxDesc(1); - GXBegin(primitive, GX_VTXFMT1, count); - } - for (i = count; i > 0; i--) { - f32 s = *(f32*) &it[0]; - f32 sx = 2.0f * (s - 0.5f); - f32 t; - f32 tx; - - if (pp->kind & TexFlipS) { - s = 1.0f - s; - } - t = *(f32*) &it[4]; - it += 8; - tx = 2.0f * (t - 0.5f); - if (pp->kind & TexFlipT) { - t = 1.0f - t; - } - GXWGFifo.f32 = x + x0 * sx + x1 * tx; - GXWGFifo.f32 = y + y0 * sx + y1 * tx; - GXWGFifo.f32 = z + z0 * sx + z1 * tx; - if (pp->kind & DispTexture) { - GXWGFifo.f32 = s; - GXWGFifo.f32 = t; - } - } - } - } -} - static inline void psDispSub(HSD_Particle* pp, u8* texform) { f32 abs_angle; @@ -1176,8 +958,220 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) up_y = mtx[1][2] * uz + t2; up_z = mtx[2][2] * uz + t4; } - psDispSubMakePolygon(pp, texform, x, y, z, pp->vel.x, pp->vel.y, pp->vel.z, - right_x, right_y, right_z, up_x, up_y, up_z); + { + f32 prev_x; + f32 prev_y; + f32 prev_z; + u8* it = texform; + + psSetCurrentMtx(GX_PNMTX0); + if (pp->kind & Trail) { + GXColor color; + + if (pp->kind & Tornado) { + calcTornadoLastPos(pp, &prev_x, &prev_y, &prev_z); + } else { + prev_x = x - pp->vel.x; + prev_y = y - pp->vel.y; + prev_z = z - pp->vel.z; + } + getClrTrail(pp, &color); + if (it == NULL) { + if (pp->kind & DispTexture) { + setVtxDesc(2); + GXBegin(GX_QUADS, GX_VTXFMT2, 4); + } else { + setVtxDesc(3); + GXBegin(GX_QUADS, GX_VTXFMT3, 4); + } + GXPosition3f32(prev_x - right_x, prev_y - right_y, + prev_z - right_z); + { + u8 a = color.a; + f32 alpha = (f32) a * pp->trail; + u8 b = color.b; + u8 g = color.g; + u8 r = color.r; + GXColor4u8(r, g, b, (u8) alpha); + } + if (pp->kind & DispTexture) { + GXWGFifo.u8 = (pp->kind >> 16) & 0xC; + } + GXPosition3f32(x - up_x, y - up_y, z - up_z); + { + u8 a = color.a; + u8 b = color.b; + u8 g = color.g; + u8 r = color.r; + GXColor4u8(r, g, b, a); + } + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; + } + GXPosition3f32(x + right_x, y + right_y, z + right_z); + { + u8 a = color.a; + u8 b = color.b; + u8 g = color.g; + u8 r = color.r; + GXColor4u8(r, g, b, a); + } + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; + } + GXPosition3f32(prev_x + up_x, prev_y + up_y, prev_z + up_z); + { + u8 a = color.a; + f32 alpha = (f32) a * pp->trail; + u8 b = color.b; + u8 g = color.g; + u8 r = color.r; + GXColor4u8(r, g, b, (u8) alpha); + } + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 3; + } + } else { + f32 trail_alpha = 255.0f * (1.0f - pp->trail); + f32 up_len = sqrtf(up_x * up_x + up_y * up_y + up_z * up_z); + + if (up_len != 0.0f) { + f32 dx = x - prev_x; + f32 dy = y - prev_y; + f32 dz = z - prev_z; + f32 xl = dx * dx; + f32 yl = dy * dy; + f32 zl = dz * dz; + f32 segment_len = sqrtf(zl + (xl + yl)); + f32 ratio = segment_len / up_len; + u32 primitive_count = *(u32*) it; + + it += sizeof(u32); + up_x *= ratio; + up_y *= ratio; + up_z *= ratio; + for (; primitive_count != 0; primitive_count--) { + GXPrimitive primitive = it[0]; + u8 count = it[1]; + s32 i; + u8 b; + u8 g; + u8 r; + + it += 4; + if (pp->kind & DispTexture) { + setVtxDesc(5); + GXBegin(primitive, GX_VTXFMT5, count); + } else { + setVtxDesc(3); + GXBegin(primitive, GX_VTXFMT3, count); + } + b = color.b; + g = color.g; + r = color.r; + for (i = count; i > 0; i--) { + f32 s = *(f32*) &it[0]; + f32 sx = 2.0f * (s - 0.5f); + f32 t; + f32 tx; + s32 converted_alpha; + s32 alpha; + + if (pp->kind & TexFlipS) { + s = 1.0f - s; + } + t = *(f32*) &it[4]; + it += 8; + converted_alpha = (s32) (255.0f - t * trail_alpha); + alpha = (s32) (255.0f - t * trail_alpha); + if (converted_alpha < 0) { + alpha = 0; + } + if (alpha > 0xFF) { + alpha = 0xFF; + } + tx = 2.0f * (t - 0.5f); + if (pp->kind & TexFlipT) { + t = 1.0f - t; + } + GXWGFifo.f32 = up_x * tx + (right_x * sx + x); + GXWGFifo.f32 = up_y * tx + (right_y * sx + y); + GXWGFifo.f32 = up_z * tx + (right_z * sx + z); + GXColor4u8(r, g, b, alpha); + if (pp->kind & DispTexture) { + GXWGFifo.f32 = s; + GXWGFifo.f32 = t; + } + } + } + } + } + } else if (it == NULL) { + if (pp->kind & DispTexture) { + setVtxDesc(0); + GXBegin(GX_QUADS, GX_VTXFMT0, 4); + } else { + setVtxDesc(1); + GXBegin(GX_QUADS, GX_VTXFMT1, 4); + } + GXPosition3f32(x - right_x, y - right_y, z - right_z); + if (pp->kind & DispTexture) { + GXWGFifo.u8 = (pp->kind >> 16) & 0xC; + } + GXPosition3f32(x - up_x, y - up_y, z - up_z); + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; + } + GXPosition3f32(x + right_x, y + right_y, z + right_z); + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; + } + GXPosition3f32(x + up_x, y + up_y, z + up_z); + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 3; + } + } else { + u32 primitive_count = *(u32*) it; + it += sizeof(u32); + for (; primitive_count != 0; primitive_count--) { + GXPrimitive primitive = it[0]; + u8 count = it[1]; + s32 i; + + it += 4; + if (pp->kind & DispTexture) { + setVtxDesc(4); + GXBegin(primitive, GX_VTXFMT4, count); + } else { + setVtxDesc(1); + GXBegin(primitive, GX_VTXFMT1, count); + } + for (i = count; i > 0; i--) { + f32 s = *(f32*) &it[0]; + f32 sx = 2.0f * (s - 0.5f); + f32 t; + f32 tx; + + if (pp->kind & TexFlipS) { + s = 1.0f - s; + } + t = *(f32*) &it[4]; + it += 8; + tx = 2.0f * (t - 0.5f); + if (pp->kind & TexFlipT) { + t = 1.0f - t; + } + GXWGFifo.f32 = x + right_x * sx + up_x * tx; + GXWGFifo.f32 = y + right_y * sx + up_y * tx; + GXWGFifo.f32 = z + right_z * sx + up_z * tx; + if (pp->kind & DispTexture) { + GXWGFifo.f32 = s; + GXWGFifo.f32 = t; + } + } + } + } + } } static inline void psScaleAppSRTAxes(HSD_Particle* pp, Mtx mtx) @@ -1605,8 +1599,8 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) by = s * old_bx + c * by; } psSetCurrentMtx(GX_PNMTX1); - /** @todo Later HSD shares this emission through an AppSRT-aware - * #psDispSubMakePolygon; Melee's emitted code lacks that body. + /** @todo Later HSD shares this emission with the polygon path; Melee's + * emitted code keeps the two separate. * @remarks Positions here stay raw @c GXWGFifo stores, not * @c GXPosition3f32: the inline's argument evaluation forces an extra * live FP register here and grows the frame past the target. */ @@ -1954,7 +1948,7 @@ void psDispParticles(u32 target_link, u32 sw) u32 prev_kind; HSD_Particle* pp; /// @todo Recover this stack space from the original inline hierarchy. - PAD_STACK(0x3c); + PAD_STACK(0xC); alpha_compare_mode = 0; prev_tex_interp_near = 0; From 6c38d120c29930d96099e9137278d4930b397e71 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 17:09:11 -0700 Subject: [PATCH 10/79] psdisp: drop the inline-depth bump and the buffer reorder With psDispSubMakePolygon spliced into psDispSub the expansion no longer nests past depth 3, so the pragma returns to its original value, and the trail buffers return to declaration order. Both are byte-identical now. --- src/sysdolphin/baselib/psdisp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 2b54ec8aba..989bcfea25 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -617,8 +617,8 @@ static inline HSD_Particle* psDispSubPoint(HSD_Particle* pp) static inline HSD_Particle* psDispSubPointTrail(HSD_Particle* pp) { - GXColor cbuf[32]; Vec3 vbuf[32]; + GXColor cbuf[32]; Vec3* p; GXColor* c; HSD_Particle* last; @@ -1919,7 +1919,7 @@ static inline void psUpdateProjectionCache(f32 perspective) #ifdef MUST_MATCH #pragma push -#pragma inline_depth(4) +#pragma inline_depth(3) #endif void psDispParticles(u32 target_link, u32 sw) { From d9972a27b9b35e8a3b3633132b6fc923f8c92085 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 17:14:35 -0700 Subject: [PATCH 11/79] psdisp: evaluate the AppSRT current transform x,y,z With psDispSubMakePolygon expanded the register pressure that previously punished this ordering is gone, so psDispSubAppSRT's cur_pos rows evaluate in the target's order and psDispSubAPPSRTPoint carries its z column instead of its x. Six real instruction-stream rows recovered (46 -> 40); 768 rows, 98.7802%. --- src/sysdolphin/baselib/psdisp.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 989bcfea25..c26dcee530 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1245,7 +1245,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) { HSD_psAppSRT* appsrt = pp->appsrt; f32 pos_y; - f32 transformed_x; + f32 transformed_z; cur_pos.x = appsrt->ssy * (pos_y = pp->pos.y); cur_pos.y = appsrt->x78 * pos_y; @@ -1253,10 +1253,10 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) cur_pos.x = appsrt->ssx * pp->pos.x + cur_pos.x; cur_pos.z = (cur_pos.y = appsrt->x74 * pp->pos.x + cur_pos.y, appsrt->x84 * pp->pos.x + cur_pos.z); - transformed_x = appsrt->x6C * pp->pos.z + cur_pos.x; - cur_pos.x = transformed_x; + cur_pos.x = appsrt->x6C * pp->pos.z + cur_pos.x; cur_pos.y = appsrt->x7C * pp->pos.z + cur_pos.y; - cur_pos.z = appsrt->x8C * pp->pos.z + cur_pos.z; + transformed_z = appsrt->x8C * pp->pos.z + cur_pos.z; + cur_pos.z = transformed_z; cur_pos.x = appsrt->x70 + cur_pos.x; cur_pos.y = appsrt->x80 + cur_pos.y; cur_pos.z = appsrt->x90 + cur_pos.z; @@ -1419,18 +1419,18 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) f32 app_cur_z; PSMTXCopy((MtxPtr) &pp->appsrt->ssx, draw_mtx); - app_cur_z = - draw_mtx[2][3] + - (draw_mtx[2][2] * pp->pos.z + - (draw_mtx[2][0] * pp->pos.x + draw_mtx[2][1] * pp->pos.y)); - app_cur_y = - draw_mtx[1][3] + - (draw_mtx[1][2] * pp->pos.z + - (draw_mtx[1][0] * pp->pos.x + draw_mtx[1][1] * pp->pos.y)); app_cur_x = draw_mtx[0][3] + (draw_mtx[0][2] * pp->pos.z + (draw_mtx[0][0] * pp->pos.x + draw_mtx[0][1] * pp->pos.y)); + app_cur_y = + draw_mtx[1][3] + + (draw_mtx[1][2] * pp->pos.z + + (draw_mtx[1][0] * pp->pos.x + draw_mtx[1][1] * pp->pos.y)); + app_cur_z = + draw_mtx[2][3] + + (draw_mtx[2][2] * pp->pos.z + + (draw_mtx[2][0] * pp->pos.x + draw_mtx[2][1] * pp->pos.y)); cur_pos.x = app_cur_x; cur_pos.y = app_cur_y; cur_pos.z = app_cur_z; @@ -1948,7 +1948,7 @@ void psDispParticles(u32 target_link, u32 sw) u32 prev_kind; HSD_Particle* pp; /// @todo Recover this stack space from the original inline hierarchy. - PAD_STACK(0xC); + PAD_STACK(0x8); alpha_compare_mode = 0; prev_tex_interp_near = 0; From a6182aec83f27e9ccf0b4886acb762c6f5e88811 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 17:19:31 -0700 Subject: [PATCH 12/79] psdisp: recover psUpdateAppSRT with a compile-time always_stamp flag The appsrt refresh was duplicated verbatim in both AppSRT display paths. They differ only in where frameNum is stamped: the point path stamps unconditionally under its own NULL guard, the polygon path only inside the changed-frame block. A constant int parameter folds away at both call sites, so one helper emits both shapes -- byte-identical output, 28 fewer lines. --- src/sysdolphin/baselib/psdisp.c | 150 +++++++++++++------------------- 1 file changed, 61 insertions(+), 89 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index c26dcee530..9af9fc56a7 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1187,6 +1187,64 @@ static inline void psScaleAppSRTAxes(HSD_Particle* pp, Mtx mtx) mtx[2][2] *= pp->size; } +/** + * @remarks @p always_stamp is a compile-time constant at both call sites: the + * point path stamps @c frameNum unconditionally under its own NULL guard, the + * polygon path only inside the changed-frame block. Both branches fold away. + */ +static inline void psUpdateAppSRT(HSD_Particle* pp, int always_stamp) +{ + if (pp->appsrt->frameNum != psFrameNum) { + f32 scale_x; + f32 scale_y; + + if (pp->appsrt->status != PS_APPSTATUS_STILL) { + HSD_psAppSRT* appsrt = pp->appsrt; + Vec3* translate = &appsrt->translate; + Vec3* rotate = (Vec3*) &appsrt->rot; + Vec3* scale = &appsrt->scale; + MtxPtr mmtx = appsrt->mmtx; + + HSD_MtxSRT(mmtx, scale, rotate, translate, NULL); + } + if (pp->appsrt->status == PS_APPSTATUS_ONCE) { + pp->appsrt->status = PS_APPSTATUS_STILL; + } + PSMTXConcat(vmtx, pp->appsrt->mmtx, (MtxPtr) &pp->appsrt->ssx); + scale_x = pp->appsrt->ssx * pp->appsrt->ssx + + pp->appsrt->x74 * pp->appsrt->x74 + + pp->appsrt->x84 * pp->appsrt->x84; + scale_x = sqrtf(scale_x); + pp->appsrt->x94 = scale_x; + scale_y = pp->appsrt->ssy * pp->appsrt->ssy + + pp->appsrt->x78 * pp->appsrt->x78 + + pp->appsrt->x88 * pp->appsrt->x88; + scale_y = sqrtf(scale_y); + pp->appsrt->x98 = scale_y; + if (pp->appsrt->xA2 != 0) { + Mtx temp_mtx; + Vec3 scale; + + PSMTXIdentity(temp_mtx); + temp_mtx[0][3] = pp->appsrt->translate.x; + temp_mtx[1][3] = pp->appsrt->translate.y; + temp_mtx[2][3] = pp->appsrt->translate.z; + PSMTXConcat(vmtx, temp_mtx, temp_mtx); + HSD_MtxGetScale(temp_mtx, &scale); + PSMTXScale((MtxPtr) &pp->appsrt->ssx, scale.x, scale.y, scale.z); + pp->appsrt->x70 = temp_mtx[0][3]; + pp->appsrt->x80 = temp_mtx[1][3]; + pp->appsrt->x90 = temp_mtx[2][3]; + } + if (!always_stamp) { + pp->appsrt->frameNum = psFrameNum; + } + } + if (always_stamp) { + pp->appsrt->frameNum = psFrameNum; + } +} + static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) { GXColor draw_color; @@ -1197,50 +1255,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) psSetCurrentMtx(GX_PNMTX1); if (pp->appsrt != NULL) { - if (pp->appsrt->frameNum != psFrameNum) { - f32 scale_x; - f32 scale_y; - - if (pp->appsrt->status != PS_APPSTATUS_STILL) { - HSD_psAppSRT* appsrt = pp->appsrt; - Vec3* translate = &appsrt->translate; - Vec3* rotate = (Vec3*) &appsrt->rot; - Vec3* scale = &appsrt->scale; - - HSD_MtxSRT(appsrt->mmtx, scale, rotate, translate, NULL); - } - if (pp->appsrt->status == PS_APPSTATUS_ONCE) { - pp->appsrt->status = PS_APPSTATUS_STILL; - } - PSMTXConcat(vmtx, pp->appsrt->mmtx, (MtxPtr) &pp->appsrt->ssx); - scale_x = pp->appsrt->ssx * pp->appsrt->ssx + - pp->appsrt->x74 * pp->appsrt->x74 + - pp->appsrt->x84 * pp->appsrt->x84; - scale_x = sqrtf(scale_x); - pp->appsrt->x94 = scale_x; - scale_y = pp->appsrt->ssy * pp->appsrt->ssy + - pp->appsrt->x78 * pp->appsrt->x78 + - pp->appsrt->x88 * pp->appsrt->x88; - scale_y = sqrtf(scale_y); - pp->appsrt->x98 = scale_y; - if (pp->appsrt->xA2 != 0) { - Mtx temp_mtx; - Vec3 scale; - - PSMTXIdentity(temp_mtx); - temp_mtx[0][3] = pp->appsrt->translate.x; - temp_mtx[1][3] = pp->appsrt->translate.y; - temp_mtx[2][3] = pp->appsrt->translate.z; - PSMTXConcat(vmtx, temp_mtx, temp_mtx); - HSD_MtxGetScale(temp_mtx, &scale); - PSMTXScale((MtxPtr) &pp->appsrt->ssx, scale.x, scale.y, - scale.z); - pp->appsrt->x70 = temp_mtx[0][3]; - pp->appsrt->x80 = temp_mtx[1][3]; - pp->appsrt->x90 = temp_mtx[2][3]; - } - } - pp->appsrt->frameNum = psFrameNum; + psUpdateAppSRT(pp, true); } { HSD_psAppSRT* appsrt = pp->appsrt; @@ -1368,50 +1383,7 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) f32 abs_angle; u8* it = texform; - if (pp->appsrt->frameNum != psFrameNum) { - f32 scale_x; - f32 scale_y; - - if (pp->appsrt->status != PS_APPSTATUS_STILL) { - HSD_psAppSRT* appsrt = pp->appsrt; - Vec3* translate = &appsrt->translate; - Vec3* rotate = (Vec3*) &appsrt->rot; - Vec3* scale = &appsrt->scale; - MtxPtr mmtx = appsrt->mmtx; - - HSD_MtxSRT(mmtx, scale, rotate, translate, NULL); - } - if (pp->appsrt->status == PS_APPSTATUS_ONCE) { - pp->appsrt->status = PS_APPSTATUS_STILL; - } - PSMTXConcat(vmtx, pp->appsrt->mmtx, (MtxPtr) &pp->appsrt->ssx); - scale_x = pp->appsrt->ssx * pp->appsrt->ssx + - pp->appsrt->x74 * pp->appsrt->x74 + - pp->appsrt->x84 * pp->appsrt->x84; - scale_x = sqrtf(scale_x); - pp->appsrt->x94 = scale_x; - scale_y = pp->appsrt->ssy * pp->appsrt->ssy + - pp->appsrt->x78 * pp->appsrt->x78 + - pp->appsrt->x88 * pp->appsrt->x88; - scale_y = sqrtf(scale_y); - pp->appsrt->x98 = scale_y; - if (pp->appsrt->xA2 != 0) { - Mtx temp_mtx; - Vec3 scale; - - PSMTXIdentity(temp_mtx); - temp_mtx[0][3] = pp->appsrt->translate.x; - temp_mtx[1][3] = pp->appsrt->translate.y; - temp_mtx[2][3] = pp->appsrt->translate.z; - PSMTXConcat(vmtx, temp_mtx, temp_mtx); - HSD_MtxGetScale(temp_mtx, &scale); - PSMTXScale((MtxPtr) &pp->appsrt->ssx, scale.x, scale.y, scale.z); - pp->appsrt->x70 = temp_mtx[0][3]; - pp->appsrt->x80 = temp_mtx[1][3]; - pp->appsrt->x90 = temp_mtx[2][3]; - } - pp->appsrt->frameNum = psFrameNum; - } + psUpdateAppSRT(pp, false); { Mtx draw_mtx; f32 app_cur_x; @@ -1948,7 +1920,7 @@ void psDispParticles(u32 target_link, u32 sw) u32 prev_kind; HSD_Particle* pp; /// @todo Recover this stack space from the original inline hierarchy. - PAD_STACK(0x8); + PAD_STACK(0x4); alpha_compare_mode = 0; prev_tex_interp_near = 0; From 9a80a837b74d25c8020ad04f702399847f1e8196 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 17:52:31 -0700 Subject: [PATCH 13/79] psdisp: declare psDispSub's up_y before up_x The axis-triple colouring rotated once the MakePolygon expansion changed the register pressure: this swap regressed in earlier rounds and now recovers 25 register rows. 743 rows, 98.8272%. --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 9af9fc56a7..435017e6ec 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -811,8 +811,8 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) f32 right_y; f32 right_x; f32 right_z; - f32 up_x; f32 up_y; + f32 up_x; f32 up_z; f32 angle; f32 x; From cab3c01c48f0197f6af796926d9d853085c326fd Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 18:41:50 -0700 Subject: [PATCH 14/79] psdisp: reorder the vertex colour and projection locals A systematic sweep of every adjacent same-type declaration pair found three levers the hand-reasoned search had missed: both vertex-emission colour runs want r, b, g rather than b, g, r, and the projection cache wants y_scale ahead of w3. Together: 743 -> 737 rows, real instruction-stream divergence 40 -> 36, register rows 252 -> 246. --- src/sysdolphin/baselib/psdisp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 435017e6ec..a670013498 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1054,9 +1054,9 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) GXPrimitive primitive = it[0]; u8 count = it[1]; s32 i; + u8 r; u8 b; u8 g; - u8 r; it += 4; if (pp->kind & DispTexture) { @@ -1678,9 +1678,9 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) GXPrimitive primitive = it[0]; u8 count = it[1]; s32 i; + u8 r; u8 b; u8 g; - u8 r; it += 4; if (pp->kind & DispTexture) { @@ -1849,8 +1849,8 @@ static inline void psUpdateProjectionCache(f32 perspective) f32 x_offset = prj[2]; f32 w1; f32 w2; - f32 w3; f32 y_scale; + f32 w3; f32 y_offset; f32 y0; f32 y1; From 5b70d2a6f84647cf84d3c1c05b3d9255d3f9bf42 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 19:20:10 -0700 Subject: [PATCH 15/79] psdisp: type the AppSRT stamp flag as bool Both call sites pass true or false, and MWCC emits byte-identical output for the narrower semantic type. --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index a670013498..a2270dcd06 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1192,7 +1192,7 @@ static inline void psScaleAppSRTAxes(HSD_Particle* pp, Mtx mtx) * point path stamps @c frameNum unconditionally under its own NULL guard, the * polygon path only inside the changed-frame block. Both branches fold away. */ -static inline void psUpdateAppSRT(HSD_Particle* pp, int always_stamp) +static inline void psUpdateAppSRT(HSD_Particle* pp, bool always_stamp) { if (pp->appsrt->frameNum != psFrameNum) { f32 scale_x; From a68579b235d89de0fdfc36dc616d5f513a033812 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 19:32:31 -0700 Subject: [PATCH 16/79] psdisp: share AppSRT scratch matrices Let both AppSRT callers own the refresh scratch matrix, and reuse the polygon path's draw matrix after refresh. This recovers the target's four-matrix ownership and dispatch order without duplicating refresh logic. The frame-neutral build remains at 98.842354% while exposing the remaining scalar-home spacing problem. --- src/sysdolphin/baselib/psdisp.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index a2270dcd06..b2f2955a0b 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1192,7 +1192,8 @@ static inline void psScaleAppSRTAxes(HSD_Particle* pp, Mtx mtx) * point path stamps @c frameNum unconditionally under its own NULL guard, the * polygon path only inside the changed-frame block. Both branches fold away. */ -static inline void psUpdateAppSRT(HSD_Particle* pp, bool always_stamp) +static inline void psUpdateAppSRT(HSD_Particle* pp, bool always_stamp, + Mtx scratch_mtx) { if (pp->appsrt->frameNum != psFrameNum) { f32 scale_x; @@ -1222,19 +1223,18 @@ static inline void psUpdateAppSRT(HSD_Particle* pp, bool always_stamp) scale_y = sqrtf(scale_y); pp->appsrt->x98 = scale_y; if (pp->appsrt->xA2 != 0) { - Mtx temp_mtx; Vec3 scale; - PSMTXIdentity(temp_mtx); - temp_mtx[0][3] = pp->appsrt->translate.x; - temp_mtx[1][3] = pp->appsrt->translate.y; - temp_mtx[2][3] = pp->appsrt->translate.z; - PSMTXConcat(vmtx, temp_mtx, temp_mtx); - HSD_MtxGetScale(temp_mtx, &scale); + PSMTXIdentity(scratch_mtx); + scratch_mtx[0][3] = pp->appsrt->translate.x; + scratch_mtx[1][3] = pp->appsrt->translate.y; + scratch_mtx[2][3] = pp->appsrt->translate.z; + PSMTXConcat(vmtx, scratch_mtx, scratch_mtx); + HSD_MtxGetScale(scratch_mtx, &scale); PSMTXScale((MtxPtr) &pp->appsrt->ssx, scale.x, scale.y, scale.z); - pp->appsrt->x70 = temp_mtx[0][3]; - pp->appsrt->x80 = temp_mtx[1][3]; - pp->appsrt->x90 = temp_mtx[2][3]; + pp->appsrt->x70 = scratch_mtx[0][3]; + pp->appsrt->x80 = scratch_mtx[1][3]; + pp->appsrt->x90 = scratch_mtx[2][3]; } if (!always_stamp) { pp->appsrt->frameNum = psFrameNum; @@ -1248,6 +1248,7 @@ static inline void psUpdateAppSRT(HSD_Particle* pp, bool always_stamp) static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) { GXColor draw_color; + Mtx scratch_mtx; Vec3 cur_pos; Vec3 prev_pos; f32 ax; @@ -1255,7 +1256,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) psSetCurrentMtx(GX_PNMTX1); if (pp->appsrt != NULL) { - psUpdateAppSRT(pp, true); + psUpdateAppSRT(pp, true, scratch_mtx); } { HSD_psAppSRT* appsrt = pp->appsrt; @@ -1372,6 +1373,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) { GXColor draw_color; + Mtx draw_mtx; Vec3 cur_pos; Vec3 prev_pos; f32 ax; @@ -1383,9 +1385,8 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) f32 abs_angle; u8* it = texform; - psUpdateAppSRT(pp, false); + psUpdateAppSRT(pp, false, draw_mtx); { - Mtx draw_mtx; f32 app_cur_x; f32 app_cur_y; f32 app_cur_z; @@ -1920,7 +1921,7 @@ void psDispParticles(u32 target_link, u32 sw) u32 prev_kind; HSD_Particle* pp; /// @todo Recover this stack space from the original inline hierarchy. - PAD_STACK(0x4); + PAD_STACK(0x34); alpha_compare_mode = 0; prev_tex_interp_near = 0; From 4ae40973d61db02b1df92a0042ab941ff5557e6f Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 19:55:03 -0700 Subject: [PATCH 17/79] psdisp: scope the rotation matrix to its branch --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index b2f2955a0b..67d117a485 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -818,7 +818,6 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) f32 x; f32 y; f32 z; - Mtx mtx; x = pp->pos.x; y = pp->pos.y; @@ -928,6 +927,7 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) abs_angle = angle; *(s32*) &abs_angle &= 0x7FFFFFFF; if (abs_angle > 0.01) { + Mtx mtx; Vec3 axis; f32 t1; From cdcc97036502b0508847d58b9f6935442eb23e5d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 19:58:15 -0700 Subject: [PATCH 18/79] psdisp: scope AppSRT draw colors to trail paths --- src/sysdolphin/baselib/psdisp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 67d117a485..87ea22be26 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1247,7 +1247,6 @@ static inline void psUpdateAppSRT(HSD_Particle* pp, bool always_stamp, static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) { - GXColor draw_color; Mtx scratch_mtx; Vec3 cur_pos; Vec3 prev_pos; @@ -1310,6 +1309,8 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) ax = pp->size > 42.5 ? 255.0f : 6.0f * pp->size; w = (s32) ax; if (pp->kind & Trail) { + GXColor draw_color; + if (prevLineWidth != (s32) (u8) w) { prevLineWidth = (u8) w; GXSetLineWidth((u8) w, GX_TO_ONE); @@ -1372,7 +1373,6 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) { - GXColor draw_color; Mtx draw_mtx; Vec3 cur_pos; Vec3 prev_pos; @@ -1578,6 +1578,8 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) * @c GXPosition3f32: the inline's argument evaluation forces an extra * live FP register here and grows the frame past the target. */ if (pp->kind & Trail) { + GXColor draw_color; + getClrTrail(pp, &draw_color); if (it == NULL) { if (pp->kind & DispTexture) { From c98a711fb37dda900741166228f186c01be24a20 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 20:21:00 -0700 Subject: [PATCH 19/79] psdisp: recover inline scratch ownership --- src/sysdolphin/baselib/psdisp.c | 45 +++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 87ea22be26..87eeb5c6e5 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -805,9 +805,15 @@ static inline void psSetCurrentMtx(GXPosNrmMtx idx) } } +static inline void psMaskAbsF32(f32* value) +{ + *(s32*) value &= 0x7FFFFFFF; +} + static inline void psDispSub(HSD_Particle* pp, u8* texform) { f32 abs_angle; + f32 abs_y; f32 right_y; f32 right_x; f32 right_z; @@ -910,8 +916,8 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) (pvmtx[1][0] * pp->vel.x + pvmtx[1][1] * pp->vel.y); } { - f32 abs_y = y; - *(s32*) &abs_y &= 0x7FFFFFFF; + abs_y = y; + psMaskAbsF32(&abs_y); if (abs_y < FLT_MIN) { angle = (x >= 0.0f) ? 1.5707964f : -1.5707964f; } else { @@ -925,7 +931,7 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) angle = pp->rotate; } abs_angle = angle; - *(s32*) &abs_angle &= 0x7FFFFFFF; + psMaskAbsF32(&abs_angle); if (abs_angle > 0.01) { Mtx mtx; Vec3 axis; @@ -1193,7 +1199,7 @@ static inline void psScaleAppSRTAxes(HSD_Particle* pp, Mtx mtx) * polygon path only inside the changed-frame block. Both branches fold away. */ static inline void psUpdateAppSRT(HSD_Particle* pp, bool always_stamp, - Mtx scratch_mtx) + Mtx scratch_mtx, Vec3* scratch_scale) { if (pp->appsrt->frameNum != psFrameNum) { f32 scale_x; @@ -1223,15 +1229,14 @@ static inline void psUpdateAppSRT(HSD_Particle* pp, bool always_stamp, scale_y = sqrtf(scale_y); pp->appsrt->x98 = scale_y; if (pp->appsrt->xA2 != 0) { - Vec3 scale; - PSMTXIdentity(scratch_mtx); scratch_mtx[0][3] = pp->appsrt->translate.x; scratch_mtx[1][3] = pp->appsrt->translate.y; scratch_mtx[2][3] = pp->appsrt->translate.z; PSMTXConcat(vmtx, scratch_mtx, scratch_mtx); - HSD_MtxGetScale(scratch_mtx, &scale); - PSMTXScale((MtxPtr) &pp->appsrt->ssx, scale.x, scale.y, scale.z); + HSD_MtxGetScale(scratch_mtx, scratch_scale); + PSMTXScale((MtxPtr) &pp->appsrt->ssx, scratch_scale->x, + scratch_scale->y, scratch_scale->z); pp->appsrt->x70 = scratch_mtx[0][3]; pp->appsrt->x80 = scratch_mtx[1][3]; pp->appsrt->x90 = scratch_mtx[2][3]; @@ -1248,6 +1253,7 @@ static inline void psUpdateAppSRT(HSD_Particle* pp, bool always_stamp, static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) { Mtx scratch_mtx; + Vec3 scratch_scale; Vec3 cur_pos; Vec3 prev_pos; f32 ax; @@ -1255,7 +1261,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) psSetCurrentMtx(GX_PNMTX1); if (pp->appsrt != NULL) { - psUpdateAppSRT(pp, true, scratch_mtx); + psUpdateAppSRT(pp, true, scratch_mtx, &scratch_scale); } { HSD_psAppSRT* appsrt = pp->appsrt; @@ -1373,8 +1379,11 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) { + f32 abs_angle; + f32 abs_v; Mtx draw_mtx; Vec3 cur_pos; + Vec3 scratch_scale; Vec3 prev_pos; f32 ax; f32 ay; @@ -1382,10 +1391,9 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) f32 by; f32 y_extent; f32 angle; - f32 abs_angle; u8* it = texform; - psUpdateAppSRT(pp, false, draw_mtx); + psUpdateAppSRT(pp, false, draw_mtx, &scratch_scale); { f32 app_cur_x; f32 app_cur_y; @@ -1545,8 +1553,8 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) } } { - f32 abs_v = vf2; - *(s32*) &abs_v &= 0x7FFFFFFF; + abs_v = vf2; + psMaskAbsF32(&abs_v); if (abs_v < FLT_MIN) { angle = (-vf1 >= 0.0f) ? 1.5707964f : -1.5707964f; } else { @@ -1560,7 +1568,7 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) angle = pp->rotate; } abs_angle = angle; - *(s32*) &abs_angle &= 0x7FFFFFFF; + psMaskAbsF32(&abs_angle); if (abs_angle > 0.01) { f32 c = cosf(angle); f32 s = sinf(angle); @@ -1913,7 +1921,6 @@ void psDispParticles(u32 target_link, u32 sw) HSD_Particle* sorted_particles; HSD_Particle* non_edge_particles; psdisp_Mtx billboard_mtx; - f32 sp700; GXTlutObj gx_tlut_obj; s32 alpha_compare_mode; s32 prev_tex_interp_near; @@ -1922,8 +1929,6 @@ void psDispParticles(u32 target_link, u32 sw) GXColor sp6D0; u32 prev_kind; HSD_Particle* pp; - /// @todo Recover this stack space from the original inline hierarchy. - PAD_STACK(0x34); alpha_compare_mode = 0; prev_tex_interp_near = 0; @@ -1967,6 +1972,9 @@ void psDispParticles(u32 target_link, u32 sw) u32 width; u32 height; + /// @todo Recover this stack space from the original inline + /// hierarchy. + PAD_STACK(0x38); if ((sw == 1) && !(pp->kind & TexEdge)) { break; } @@ -2106,8 +2114,7 @@ void psDispParticles(u32 target_link, u32 sw) sp7B0 = NULL; PSMTXScale(temp_mtx, scale_s, scale_t, 1.0f); if (pp->kind & MirrorT) { - sp700 = 1.0f; - temp_mtx[1][3] = sp700; + temp_mtx[1][3] = 1.0f; } GXLoadTexMtxImm(temp_mtx, GX_TEXMTX0, GX_MTX2x4); GXSetTexCoordGen2(GX_TEXCOORD0, GX_TG_MTX2x4, From 16de082c6651724a7e262ca1b06c58adce31cb93 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 20:55:54 -0700 Subject: [PATCH 20/79] psdisp: restore AppSRT axis ownership --- src/sysdolphin/baselib/psdisp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 87eeb5c6e5..fc619aabba 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1447,16 +1447,17 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) } psScaleAppSRTAxes(pp, draw_mtx); } - ax = pp->appsrt->x94 * pp->size; - bx = ax; + bx = pp->appsrt->x94 * pp->size; y_extent = pp->appsrt->x98 * pp->size; if (it == NULL) { ay = y_extent; by = -ay; + ax = bx; } else { ay = 0.0f; + ax = bx; by = -y_extent; - bx = 0.0f; + bx = ay; } if ((pp->kind & Trail) || (pp->kind & DirVec)) { f32 vf1; From 66f3c18363b4c44135087c3fdd8806e1cb06ec59 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 21:02:08 -0700 Subject: [PATCH 21/79] psdisp: restore the polygon helper boundary --- src/sysdolphin/baselib/psdisp.c | 434 ++++++++++++++++---------------- 1 file changed, 220 insertions(+), 214 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index fc619aabba..2377a09108 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -805,6 +805,224 @@ static inline void psSetCurrentMtx(GXPosNrmMtx idx) } } +static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, + f32 y, f32 z, f32 ppvx, f32 ppvy, + f32 ppvz, f32 x0, f32 y0, f32 z0, + f32 x1, f32 y1, f32 z1) +{ + f32 prev_x; + f32 prev_y; + f32 prev_z; + u8* it = texform; + + psSetCurrentMtx(GX_PNMTX0); + if (pp->kind & Trail) { + GXColor color; + + if (pp->kind & Tornado) { + calcTornadoLastPos(pp, &prev_x, &prev_y, &prev_z); + } else { + prev_x = x - ppvx; + prev_y = y - ppvy; + prev_z = z - ppvz; + } + getClrTrail(pp, &color); + if (it == NULL) { + if (pp->kind & DispTexture) { + setVtxDesc(2); + GXBegin(GX_QUADS, GX_VTXFMT2, 4); + } else { + setVtxDesc(3); + GXBegin(GX_QUADS, GX_VTXFMT3, 4); + } + GXPosition3f32(prev_x - x0, prev_y - y0, prev_z - z0); + { + u8 a = color.a; + f32 alpha = (f32) a * pp->trail; + u8 b = color.b; + u8 g = color.g; + u8 r = color.r; + GXColor4u8(r, g, b, (u8) alpha); + } + if (pp->kind & DispTexture) { + GXWGFifo.u8 = (pp->kind >> 16) & 0xC; + } + GXPosition3f32(x - x1, y - y1, z - z1); + { + u8 a = color.a; + u8 b = color.b; + u8 g = color.g; + u8 r = color.r; + GXColor4u8(r, g, b, a); + } + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; + } + GXPosition3f32(x + x0, y + y0, z + z0); + { + u8 a = color.a; + u8 b = color.b; + u8 g = color.g; + u8 r = color.r; + GXColor4u8(r, g, b, a); + } + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; + } + GXPosition3f32(prev_x + x1, prev_y + y1, prev_z + z1); + { + u8 a = color.a; + f32 alpha = (f32) a * pp->trail; + u8 b = color.b; + u8 g = color.g; + u8 r = color.r; + GXColor4u8(r, g, b, (u8) alpha); + } + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 3; + } + } else { + f32 trail_alpha = 255.0f * (1.0f - pp->trail); + f32 up_len = sqrtf(x1 * x1 + y1 * y1 + z1 * z1); + + if (up_len != 0.0f) { + f32 dx = x - prev_x; + f32 dy = y - prev_y; + f32 dz = z - prev_z; + f32 xl = dx * dx; + f32 yl = dy * dy; + f32 zl = dz * dz; + f32 segment_len = sqrtf(zl + (xl + yl)); + f32 ratio = segment_len / up_len; + u32 primitive_count = *(u32*) it; + + it += sizeof(u32); + x1 *= ratio; + y1 *= ratio; + z1 *= ratio; + for (; primitive_count != 0; primitive_count--) { + GXPrimitive primitive = it[0]; + u8 count = it[1]; + s32 i; + u8 r; + u8 b; + u8 g; + + it += 4; + if (pp->kind & DispTexture) { + setVtxDesc(5); + GXBegin(primitive, GX_VTXFMT5, count); + } else { + setVtxDesc(3); + GXBegin(primitive, GX_VTXFMT3, count); + } + b = color.b; + g = color.g; + r = color.r; + for (i = count; i > 0; i--) { + f32 s = *(f32*) &it[0]; + f32 sx = 2.0f * (s - 0.5f); + f32 t; + f32 tx; + s32 converted_alpha; + s32 alpha; + + if (pp->kind & TexFlipS) { + s = 1.0f - s; + } + t = *(f32*) &it[4]; + it += 8; + converted_alpha = (s32) (255.0f - t * trail_alpha); + alpha = (s32) (255.0f - t * trail_alpha); + if (converted_alpha < 0) { + alpha = 0; + } + if (alpha > 0xFF) { + alpha = 0xFF; + } + tx = 2.0f * (t - 0.5f); + if (pp->kind & TexFlipT) { + t = 1.0f - t; + } + GXWGFifo.f32 = x1 * tx + (x0 * sx + x); + GXWGFifo.f32 = y1 * tx + (y0 * sx + y); + GXWGFifo.f32 = z1 * tx + (z0 * sx + z); + GXColor4u8(r, g, b, alpha); + if (pp->kind & DispTexture) { + GXWGFifo.f32 = s; + GXWGFifo.f32 = t; + } + } + } + } + } + } else if (it == NULL) { + if (pp->kind & DispTexture) { + setVtxDesc(0); + GXBegin(GX_QUADS, GX_VTXFMT0, 4); + } else { + setVtxDesc(1); + GXBegin(GX_QUADS, GX_VTXFMT1, 4); + } + GXPosition3f32(x - x0, y - y0, z - z0); + if (pp->kind & DispTexture) { + GXWGFifo.u8 = (pp->kind >> 16) & 0xC; + } + GXPosition3f32(x - x1, y - y1, z - z1); + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; + } + GXPosition3f32(x + x0, y + y0, z + z0); + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; + } + GXPosition3f32(x + x1, y + y1, z + z1); + if (pp->kind & DispTexture) { + GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 3; + } + } else { + u32 primitive_count = *(u32*) it; + it += sizeof(u32); + for (; primitive_count != 0; primitive_count--) { + GXPrimitive primitive = it[0]; + u8 count = it[1]; + s32 i; + + it += 4; + if (pp->kind & DispTexture) { + setVtxDesc(4); + GXBegin(primitive, GX_VTXFMT4, count); + } else { + setVtxDesc(1); + GXBegin(primitive, GX_VTXFMT1, count); + } + for (i = count; i > 0; i--) { + f32 s = *(f32*) &it[0]; + f32 sx = 2.0f * (s - 0.5f); + f32 t; + f32 tx; + + if (pp->kind & TexFlipS) { + s = 1.0f - s; + } + t = *(f32*) &it[4]; + it += 8; + tx = 2.0f * (t - 0.5f); + if (pp->kind & TexFlipT) { + t = 1.0f - t; + } + GXWGFifo.f32 = x + x0 * sx + x1 * tx; + GXWGFifo.f32 = y + y0 * sx + y1 * tx; + GXWGFifo.f32 = z + z0 * sx + z1 * tx; + if (pp->kind & DispTexture) { + GXWGFifo.f32 = s; + GXWGFifo.f32 = t; + } + } + } + } +} + static inline void psMaskAbsF32(f32* value) { *(s32*) value &= 0x7FFFFFFF; @@ -964,220 +1182,8 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) up_y = mtx[1][2] * uz + t2; up_z = mtx[2][2] * uz + t4; } - { - f32 prev_x; - f32 prev_y; - f32 prev_z; - u8* it = texform; - - psSetCurrentMtx(GX_PNMTX0); - if (pp->kind & Trail) { - GXColor color; - - if (pp->kind & Tornado) { - calcTornadoLastPos(pp, &prev_x, &prev_y, &prev_z); - } else { - prev_x = x - pp->vel.x; - prev_y = y - pp->vel.y; - prev_z = z - pp->vel.z; - } - getClrTrail(pp, &color); - if (it == NULL) { - if (pp->kind & DispTexture) { - setVtxDesc(2); - GXBegin(GX_QUADS, GX_VTXFMT2, 4); - } else { - setVtxDesc(3); - GXBegin(GX_QUADS, GX_VTXFMT3, 4); - } - GXPosition3f32(prev_x - right_x, prev_y - right_y, - prev_z - right_z); - { - u8 a = color.a; - f32 alpha = (f32) a * pp->trail; - u8 b = color.b; - u8 g = color.g; - u8 r = color.r; - GXColor4u8(r, g, b, (u8) alpha); - } - if (pp->kind & DispTexture) { - GXWGFifo.u8 = (pp->kind >> 16) & 0xC; - } - GXPosition3f32(x - up_x, y - up_y, z - up_z); - { - u8 a = color.a; - u8 b = color.b; - u8 g = color.g; - u8 r = color.r; - GXColor4u8(r, g, b, a); - } - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; - } - GXPosition3f32(x + right_x, y + right_y, z + right_z); - { - u8 a = color.a; - u8 b = color.b; - u8 g = color.g; - u8 r = color.r; - GXColor4u8(r, g, b, a); - } - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; - } - GXPosition3f32(prev_x + up_x, prev_y + up_y, prev_z + up_z); - { - u8 a = color.a; - f32 alpha = (f32) a * pp->trail; - u8 b = color.b; - u8 g = color.g; - u8 r = color.r; - GXColor4u8(r, g, b, (u8) alpha); - } - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 3; - } - } else { - f32 trail_alpha = 255.0f * (1.0f - pp->trail); - f32 up_len = sqrtf(up_x * up_x + up_y * up_y + up_z * up_z); - - if (up_len != 0.0f) { - f32 dx = x - prev_x; - f32 dy = y - prev_y; - f32 dz = z - prev_z; - f32 xl = dx * dx; - f32 yl = dy * dy; - f32 zl = dz * dz; - f32 segment_len = sqrtf(zl + (xl + yl)); - f32 ratio = segment_len / up_len; - u32 primitive_count = *(u32*) it; - - it += sizeof(u32); - up_x *= ratio; - up_y *= ratio; - up_z *= ratio; - for (; primitive_count != 0; primitive_count--) { - GXPrimitive primitive = it[0]; - u8 count = it[1]; - s32 i; - u8 r; - u8 b; - u8 g; - - it += 4; - if (pp->kind & DispTexture) { - setVtxDesc(5); - GXBegin(primitive, GX_VTXFMT5, count); - } else { - setVtxDesc(3); - GXBegin(primitive, GX_VTXFMT3, count); - } - b = color.b; - g = color.g; - r = color.r; - for (i = count; i > 0; i--) { - f32 s = *(f32*) &it[0]; - f32 sx = 2.0f * (s - 0.5f); - f32 t; - f32 tx; - s32 converted_alpha; - s32 alpha; - - if (pp->kind & TexFlipS) { - s = 1.0f - s; - } - t = *(f32*) &it[4]; - it += 8; - converted_alpha = (s32) (255.0f - t * trail_alpha); - alpha = (s32) (255.0f - t * trail_alpha); - if (converted_alpha < 0) { - alpha = 0; - } - if (alpha > 0xFF) { - alpha = 0xFF; - } - tx = 2.0f * (t - 0.5f); - if (pp->kind & TexFlipT) { - t = 1.0f - t; - } - GXWGFifo.f32 = up_x * tx + (right_x * sx + x); - GXWGFifo.f32 = up_y * tx + (right_y * sx + y); - GXWGFifo.f32 = up_z * tx + (right_z * sx + z); - GXColor4u8(r, g, b, alpha); - if (pp->kind & DispTexture) { - GXWGFifo.f32 = s; - GXWGFifo.f32 = t; - } - } - } - } - } - } else if (it == NULL) { - if (pp->kind & DispTexture) { - setVtxDesc(0); - GXBegin(GX_QUADS, GX_VTXFMT0, 4); - } else { - setVtxDesc(1); - GXBegin(GX_QUADS, GX_VTXFMT1, 4); - } - GXPosition3f32(x - right_x, y - right_y, z - right_z); - if (pp->kind & DispTexture) { - GXWGFifo.u8 = (pp->kind >> 16) & 0xC; - } - GXPosition3f32(x - up_x, y - up_y, z - up_z); - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; - } - GXPosition3f32(x + right_x, y + right_y, z + right_z); - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; - } - GXPosition3f32(x + up_x, y + up_y, z + up_z); - if (pp->kind & DispTexture) { - GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 3; - } - } else { - u32 primitive_count = *(u32*) it; - it += sizeof(u32); - for (; primitive_count != 0; primitive_count--) { - GXPrimitive primitive = it[0]; - u8 count = it[1]; - s32 i; - - it += 4; - if (pp->kind & DispTexture) { - setVtxDesc(4); - GXBegin(primitive, GX_VTXFMT4, count); - } else { - setVtxDesc(1); - GXBegin(primitive, GX_VTXFMT1, count); - } - for (i = count; i > 0; i--) { - f32 s = *(f32*) &it[0]; - f32 sx = 2.0f * (s - 0.5f); - f32 t; - f32 tx; - - if (pp->kind & TexFlipS) { - s = 1.0f - s; - } - t = *(f32*) &it[4]; - it += 8; - tx = 2.0f * (t - 0.5f); - if (pp->kind & TexFlipT) { - t = 1.0f - t; - } - GXWGFifo.f32 = x + right_x * sx + up_x * tx; - GXWGFifo.f32 = y + right_y * sx + up_y * tx; - GXWGFifo.f32 = z + right_z * sx + up_z * tx; - if (pp->kind & DispTexture) { - GXWGFifo.f32 = s; - GXWGFifo.f32 = t; - } - } - } - } - } + psDispSubMakePolygon(pp, texform, x, y, z, pp->vel.x, pp->vel.y, pp->vel.z, + right_x, right_y, right_z, up_x, up_y, up_z); } static inline void psScaleAppSRTAxes(HSD_Particle* pp, Mtx mtx) From 1a12397e5b37d4419709fdee9f58080271437805 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 21:04:30 -0700 Subject: [PATCH 22/79] psdisp: keep polygon velocity loads branch-local --- src/sysdolphin/baselib/psdisp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 2377a09108..8229139435 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -822,9 +822,9 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & Tornado) { calcTornadoLastPos(pp, &prev_x, &prev_y, &prev_z); } else { - prev_x = x - ppvx; - prev_y = y - ppvy; - prev_z = z - ppvz; + prev_x = x - pp->vel.x; + prev_y = y - pp->vel.y; + prev_z = z - pp->vel.z; } getClrTrail(pp, &color); if (it == NULL) { From 0f536b73a48909df68d9497aebb36917665d2f02 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 21:22:22 -0700 Subject: [PATCH 23/79] psdisp: restore AppSRT coefficient order --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 8229139435..f06c995059 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1499,9 +1499,9 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) w0 = pp->appsrt->x90 + (pp->appsrt->x8C * pp->pos.z + (pp->appsrt->x84 * pp->pos.x + pp->appsrt->x88 * pp->pos.y)); + f16 = prj[1] * pp->appsrt->x6C + prj[2] * pp->appsrt->x8C; s808 = prj[1] * pp->appsrt->ssx + prj[2] * pp->appsrt->x84; s804 = prj[1] * pp->appsrt->ssy + prj[2] * pp->appsrt->x88; - f16 = prj[1] * pp->appsrt->x6C + prj[2] * pp->appsrt->x8C; f20 = prj[1] * pp->appsrt->x70 + prj[2] * pp->appsrt->x90; f12 = prj[3] * pp->appsrt->x74 + prj[4] * pp->appsrt->x84; f8 = prj[3] * pp->appsrt->x78 + prj[4] * pp->appsrt->x88; From dbf6e4336cd93e2b5d6167d0ab264399dca1a708 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 22:28:03 -0700 Subject: [PATCH 24/79] psdisp: restore axis declaration order --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index f06c995059..ffadcaacc6 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1035,8 +1035,8 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) f32 right_y; f32 right_x; f32 right_z; - f32 up_y; f32 up_x; + f32 up_y; f32 up_z; f32 angle; f32 x; From 500da0232b44e3578e72d2f02d8c6cd2f434c467 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 22:40:23 -0700 Subject: [PATCH 25/79] psdisp: recover AppSRT extent ownership --- src/sysdolphin/baselib/psdisp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index ffadcaacc6..be5b2549b7 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1391,11 +1391,12 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) Vec3 cur_pos; Vec3 scratch_scale; Vec3 prev_pos; + f32 x_extent; + f32 y_extent; f32 ax; f32 ay; f32 bx; f32 by; - f32 y_extent; f32 angle; u8* it = texform; @@ -1453,15 +1454,16 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) } psScaleAppSRTAxes(pp, draw_mtx); } - bx = pp->appsrt->x94 * pp->size; + x_extent = pp->appsrt->x94 * pp->size; y_extent = pp->appsrt->x98 * pp->size; if (it == NULL) { ay = y_extent; by = -ay; + bx = x_extent; ax = bx; } else { ay = 0.0f; - ax = bx; + ax = x_extent; by = -y_extent; bx = ay; } From 03875b6bc3bc8da59dda700eee7ed171e86fcec8 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 22:44:13 -0700 Subject: [PATCH 26/79] psdisp: restore root state declaration order --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index be5b2549b7..a937b6fa77 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1915,13 +1915,13 @@ static inline void psUpdateProjectionCache(f32 perspective) #endif void psDispParticles(u32 target_link, u32 sw) { - s32 needs_setup; s32 sp7B4; void* sp7B0; u32 sp7AC; u32 sp7A8; u8 sp7A5; u8 sp7A4; + s32 needs_setup; GXColor chan_amb_color; void* sp79C; psdisp_Tlut tlut_obj; From 8346567b8b8e01194308c82d6b54e59b532458fb Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 22:50:41 -0700 Subject: [PATCH 27/79] psdisp: restore AppSRT coefficient lifetime order --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index a937b6fa77..41b8a43077 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1501,9 +1501,9 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) w0 = pp->appsrt->x90 + (pp->appsrt->x8C * pp->pos.z + (pp->appsrt->x84 * pp->pos.x + pp->appsrt->x88 * pp->pos.y)); - f16 = prj[1] * pp->appsrt->x6C + prj[2] * pp->appsrt->x8C; s808 = prj[1] * pp->appsrt->ssx + prj[2] * pp->appsrt->x84; s804 = prj[1] * pp->appsrt->ssy + prj[2] * pp->appsrt->x88; + f16 = prj[1] * pp->appsrt->x6C + prj[2] * pp->appsrt->x8C; f20 = prj[1] * pp->appsrt->x70 + prj[2] * pp->appsrt->x90; f12 = prj[3] * pp->appsrt->x74 + prj[4] * pp->appsrt->x84; f8 = prj[3] * pp->appsrt->x78 + prj[4] * pp->appsrt->x88; From 07b794fbd0f939f7f1d32e70c37be9c1acbdd113 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 22:51:22 -0700 Subject: [PATCH 28/79] psdisp: chain the AppSRT null-form extents --- src/sysdolphin/baselib/psdisp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 41b8a43077..b10db61381 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1459,8 +1459,7 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) if (it == NULL) { ay = y_extent; by = -ay; - bx = x_extent; - ax = bx; + ax = bx = x_extent; } else { ay = 0.0f; ax = x_extent; From 7d756fd3af9aed2af6ce223b804fe9ad7463f165 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 23:00:12 -0700 Subject: [PATCH 29/79] psdisp: invalidate image cache after palette selection --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index b10db61381..4719868a6f 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -2169,8 +2169,8 @@ void psDispParticles(u32 target_link, u32 sw) tlut_obj.n_entries); GXLoadTlut(&gx_tlut_obj, tlut_obj.tlut_name); - sp7B0 = NULL; } + sp7B0 = NULL; } } } From 962eeb52f4a0db60c8ff24214abfc9855d43a74b Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 23:04:36 -0700 Subject: [PATCH 30/79] psdisp: recover polygon up-axis ownership --- src/sysdolphin/baselib/psdisp.c | 39 +++++++++++++++++---------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 4719868a6f..604b980502 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1,7 +1,5 @@ #include "psdisp.h" -#include - #include "baselib/cobj.h" #include "baselib/fog.h" @@ -813,8 +811,14 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, f32 prev_x; f32 prev_y; f32 prev_z; + Vec2 up; + f32 up_z; u8* it = texform; + up.x = x1; + up.y = y1; + up_z = z1; + psSetCurrentMtx(GX_PNMTX0); if (pp->kind & Trail) { GXColor color; @@ -847,7 +851,7 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & DispTexture) { GXWGFifo.u8 = (pp->kind >> 16) & 0xC; } - GXPosition3f32(x - x1, y - y1, z - z1); + GXPosition3f32(x - up.x, y - up.y, z - up_z); { u8 a = color.a; u8 b = color.b; @@ -869,7 +873,7 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; } - GXPosition3f32(prev_x + x1, prev_y + y1, prev_z + z1); + GXPosition3f32(prev_x + up.x, prev_y + up.y, prev_z + up_z); { u8 a = color.a; f32 alpha = (f32) a * pp->trail; @@ -883,7 +887,7 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, } } else { f32 trail_alpha = 255.0f * (1.0f - pp->trail); - f32 up_len = sqrtf(x1 * x1 + y1 * y1 + z1 * z1); + f32 up_len = sqrtf(up.x * up.x + up.y * up.y + up_z * up_z); if (up_len != 0.0f) { f32 dx = x - prev_x; @@ -897,9 +901,9 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, u32 primitive_count = *(u32*) it; it += sizeof(u32); - x1 *= ratio; - y1 *= ratio; - z1 *= ratio; + up.x *= ratio; + up.y *= ratio; + up_z *= ratio; for (; primitive_count != 0; primitive_count--) { GXPrimitive primitive = it[0]; u8 count = it[1]; @@ -944,9 +948,9 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & TexFlipT) { t = 1.0f - t; } - GXWGFifo.f32 = x1 * tx + (x0 * sx + x); - GXWGFifo.f32 = y1 * tx + (y0 * sx + y); - GXWGFifo.f32 = z1 * tx + (z0 * sx + z); + GXWGFifo.f32 = up.x * tx + (x0 * sx + x); + GXWGFifo.f32 = up.y * tx + (y0 * sx + y); + GXWGFifo.f32 = up_z * tx + (z0 * sx + z); GXColor4u8(r, g, b, alpha); if (pp->kind & DispTexture) { GXWGFifo.f32 = s; @@ -968,7 +972,7 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & DispTexture) { GXWGFifo.u8 = (pp->kind >> 16) & 0xC; } - GXPosition3f32(x - x1, y - y1, z - z1); + GXPosition3f32(x - up.x, y - up.y, z - up_z); if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; } @@ -976,7 +980,7 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; } - GXPosition3f32(x + x1, y + y1, z + z1); + GXPosition3f32(x + up.x, y + up.y, z + up_z); if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 3; } @@ -1011,9 +1015,9 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & TexFlipT) { t = 1.0f - t; } - GXWGFifo.f32 = x + x0 * sx + x1 * tx; - GXWGFifo.f32 = y + y0 * sx + y1 * tx; - GXWGFifo.f32 = z + z0 * sx + z1 * tx; + GXWGFifo.f32 = x + x0 * sx + up.x * tx; + GXWGFifo.f32 = y + y0 * sx + up.y * tx; + GXWGFifo.f32 = z + z0 * sx + up_z * tx; if (pp->kind & DispTexture) { GXWGFifo.f32 = s; GXWGFifo.f32 = t; @@ -1980,9 +1984,6 @@ void psDispParticles(u32 target_link, u32 sw) u32 width; u32 height; - /// @todo Recover this stack space from the original inline - /// hierarchy. - PAD_STACK(0x38); if ((sw == 1) && !(pp->kind & TexEdge)) { break; } From 400ba30401931bc0d454ef93a2c1f47bf29caee6 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 23:08:15 -0700 Subject: [PATCH 31/79] psdisp: use particle colors directly in form loops --- src/sysdolphin/baselib/psdisp.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 604b980502..268a94f4f6 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -908,9 +908,6 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, GXPrimitive primitive = it[0]; u8 count = it[1]; s32 i; - u8 r; - u8 b; - u8 g; it += 4; if (pp->kind & DispTexture) { @@ -920,9 +917,6 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, setVtxDesc(3); GXBegin(primitive, GX_VTXFMT3, count); } - b = color.b; - g = color.g; - r = color.r; for (i = count; i > 0; i--) { f32 s = *(f32*) &it[0]; f32 sx = 2.0f * (s - 0.5f); @@ -951,7 +945,7 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, GXWGFifo.f32 = up.x * tx + (x0 * sx + x); GXWGFifo.f32 = up.y * tx + (y0 * sx + y); GXWGFifo.f32 = up_z * tx + (z0 * sx + z); - GXColor4u8(r, g, b, alpha); + GXColor4u8(color.r, color.g, color.b, alpha); if (pp->kind & DispTexture) { GXWGFifo.f32 = s; GXWGFifo.f32 = t; @@ -1701,9 +1695,6 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) GXPrimitive primitive = it[0]; u8 count = it[1]; s32 i; - u8 r; - u8 b; - u8 g; it += 4; if (pp->kind & DispTexture) { @@ -1713,9 +1704,6 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) setVtxDesc(3); GXBegin(primitive, GX_VTXFMT3, count); } - b = draw_color.b; - g = draw_color.g; - r = draw_color.r; for (i = count; i > 0; i--) { f32 s = *(f32*) &it[0]; f32 sx = 2.0f * (s - 0.5f); @@ -1744,7 +1732,8 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) GXWGFifo.f32 = cur_pos.x + ax * sx + bx * tx; GXWGFifo.f32 = cur_pos.y + ay * sx + by * tx; GXWGFifo.f32 = cur_pos.z; - GXColor4u8(r, g, b, (u8) alpha); + GXColor4u8(draw_color.r, draw_color.g, draw_color.b, + (u8) alpha); if (pp->kind & DispTexture) { GXWGFifo.f32 = s; GXWGFifo.f32 = t; From 40d33a8ed0272dcfbb3076909a548789a198975f Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 23:14:07 -0700 Subject: [PATCH 32/79] psdisp: preserve the AppSRT point X transform --- src/sysdolphin/baselib/psdisp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 268a94f4f6..83d336d178 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1270,6 +1270,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) { HSD_psAppSRT* appsrt = pp->appsrt; f32 pos_y; + f32 transformed_x; f32 transformed_z; cur_pos.x = appsrt->ssy * (pos_y = pp->pos.y); @@ -1278,7 +1279,8 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) cur_pos.x = appsrt->ssx * pp->pos.x + cur_pos.x; cur_pos.z = (cur_pos.y = appsrt->x74 * pp->pos.x + cur_pos.y, appsrt->x84 * pp->pos.x + cur_pos.z); - cur_pos.x = appsrt->x6C * pp->pos.z + cur_pos.x; + transformed_x = appsrt->x6C * pp->pos.z + cur_pos.x; + cur_pos.x = transformed_x; cur_pos.y = appsrt->x7C * pp->pos.z + cur_pos.y; transformed_z = appsrt->x8C * pp->pos.z + cur_pos.z; cur_pos.z = transformed_z; From 9909570fb1fb9aba33c943c47499fc71e6a2f379 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 23:29:28 -0700 Subject: [PATCH 33/79] psdisp: preserve AppSRT position components --- src/sysdolphin/baselib/psdisp.c | 43 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 83d336d178..a14ff3ffb4 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1269,21 +1269,17 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) } { HSD_psAppSRT* appsrt = pp->appsrt; - f32 pos_y; - f32 transformed_x; - f32 transformed_z; + f32 y_component; - cur_pos.x = appsrt->ssy * (pos_y = pp->pos.y); - cur_pos.y = appsrt->x78 * pos_y; - cur_pos.z = appsrt->x88 * pos_y; + cur_pos.x = appsrt->ssy * (y_component = pp->pos.y); + cur_pos.y = appsrt->x78 * y_component; + cur_pos.z = appsrt->x88 * y_component; cur_pos.x = appsrt->ssx * pp->pos.x + cur_pos.x; - cur_pos.z = (cur_pos.y = appsrt->x74 * pp->pos.x + cur_pos.y, + cur_pos.z = (y_component = appsrt->x74 * pp->pos.x + cur_pos.y, appsrt->x84 * pp->pos.x + cur_pos.z); - transformed_x = appsrt->x6C * pp->pos.z + cur_pos.x; - cur_pos.x = transformed_x; - cur_pos.y = appsrt->x7C * pp->pos.z + cur_pos.y; - transformed_z = appsrt->x8C * pp->pos.z + cur_pos.z; - cur_pos.z = transformed_z; + cur_pos.x = appsrt->x6C * pp->pos.z + cur_pos.x; + cur_pos.y = appsrt->x7C * pp->pos.z + y_component; + cur_pos.z = appsrt->x8C * pp->pos.z + cur_pos.z; cur_pos.x = appsrt->x70 + cur_pos.x; cur_pos.y = appsrt->x80 + cur_pos.y; cur_pos.z = appsrt->x90 + cur_pos.z; @@ -1405,20 +1401,19 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) f32 app_cur_x; f32 app_cur_y; f32 app_cur_z; + f32 pos_x; + f32 pos_y; PSMTXCopy((MtxPtr) &pp->appsrt->ssx, draw_mtx); - app_cur_x = - draw_mtx[0][3] + - (draw_mtx[0][2] * pp->pos.z + - (draw_mtx[0][0] * pp->pos.x + draw_mtx[0][1] * pp->pos.y)); - app_cur_y = - draw_mtx[1][3] + - (draw_mtx[1][2] * pp->pos.z + - (draw_mtx[1][0] * pp->pos.x + draw_mtx[1][1] * pp->pos.y)); - app_cur_z = - draw_mtx[2][3] + - (draw_mtx[2][2] * pp->pos.z + - (draw_mtx[2][0] * pp->pos.x + draw_mtx[2][1] * pp->pos.y)); + app_cur_x = draw_mtx[0][3] + (draw_mtx[0][2] * pp->pos.z + + (draw_mtx[0][0] * (pos_x = pp->pos.x) + + draw_mtx[0][1] * (pos_y = pp->pos.y))); + app_cur_y = draw_mtx[1][3] + + (draw_mtx[1][2] * pp->pos.z + + (draw_mtx[1][0] * pos_x + draw_mtx[1][1] * pos_y)); + app_cur_z = draw_mtx[2][3] + + (draw_mtx[2][2] * pp->pos.z + + (draw_mtx[2][0] * pos_x + draw_mtx[2][1] * pos_y)); cur_pos.x = app_cur_x; cur_pos.y = app_cur_y; cur_pos.z = app_cur_z; From 43f50700ad6c05cf7e69c87b237b1fc2962645ac Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 28 Aug 2026 23:53:39 -0700 Subject: [PATCH 34/79] psdisp: preserve nonperspective vector components --- src/sysdolphin/baselib/psdisp.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index a14ff3ffb4..5769eb7674 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1541,16 +1541,24 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) f32 tz; calcTornadoLastPos(pp, &tx, &ty, &tz); { - f32 dy = pp->pos.y - ty; - f32 dx = pp->pos.x - tx; - f32 dz = pp->pos.z - tz; + f32 dx; + f32 dy; + f32 dz; + + dy = pp->pos.y - ty; + dx = pp->pos.x - tx; + dz = pp->pos.z - tz; vf1 = s7F8 * dz + (s800 * dx + s7FC * dy); vf2 = f20 * dz + (f17 * dx + f18 * dy); } } else { - f32 vy = pp->vel.y; - f32 vx = pp->vel.x; - f32 vz = pp->vel.z; + f32 vz; + f32 vx; + f32 vy; + + vy = pp->vel.y; + vx = pp->vel.x; + vz = pp->vel.z; vf1 = s7F8 * vz + (s800 * vx + s7FC * vy); vf2 = f20 * vz + (f17 * vx + f18 * vy); } From 33a8c5a8e1d41c4c04c6fb6ca252258c57d5f59a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 00:45:57 -0700 Subject: [PATCH 35/79] psdisp: pass cached ambient color directly --- src/sysdolphin/baselib/psdisp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 5769eb7674..76f87c71bd 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1919,7 +1919,6 @@ void psDispParticles(u32 target_link, u32 sw) u8 sp7A5; u8 sp7A4; s32 needs_setup; - GXColor chan_amb_color; void* sp79C; psdisp_Tlut tlut_obj; GXColor chan_mat_color; @@ -1998,8 +1997,7 @@ void psDispParticles(u32 target_link, u32 sw) prevChanMat.a = prevChanAmb.a = 0xFF; chan_mat_color = prevChanMat; GXSetChanMatColor(GX_COLOR0A0, chan_mat_color); - chan_amb_color = prevChanAmb; - GXSetChanAmbColor(GX_COLOR0A0, chan_amb_color); + GXSetChanAmbColor(GX_COLOR0A0, prevChanAmb); psSetupTevInvalidState(); psSetupTevCommon(); psSetColor(&prevColorPrim, 0xFF); From a4c24d20394798a753f06fe9b46a0e5a85676bee Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 00:58:46 -0700 Subject: [PATCH 36/79] psdisp: recover symmetric polygon axis ownership --- src/sysdolphin/baselib/psdisp.c | 34 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 76f87c71bd..4efd860ca2 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -524,12 +524,10 @@ static inline HSD_Particle* psDispSubPoint(HSD_Particle* pp) HSD_Particle* q; s32 count; s32 i; - f32 fw; s32 w; psSetCurrentMtx(GX_PNMTX0); - fw = (pp->size > 42.5) ? 255.0f : 6.0f * pp->size; - w = (s32) fw; + w = (s32) ((pp->size > 42.5) ? 255.0f : 6.0f * pp->size); if (prevPointSize != (s32) (u8) w) { prevPointSize = (u8) w; GXSetPointSize((u8) w, GX_TO_ONE); @@ -623,12 +621,10 @@ static inline HSD_Particle* psDispSubPointTrail(HSD_Particle* pp) HSD_Particle* q; s32 count; s32 i; - f32 fw; s32 w; psSetCurrentMtx(GX_PNMTX0); - fw = (pp->size > 42.5) ? 255.0f : 6.0f * pp->size; - w = (s32) fw; + w = (s32) ((pp->size > 42.5) ? 255.0f : 6.0f * pp->size); if (prevLineWidth != (s32) (u8) w) { prevLineWidth = (u8) w; GXSetLineWidth((u8) w, GX_TO_ONE); @@ -811,10 +807,15 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, f32 prev_x; f32 prev_y; f32 prev_z; + Vec2 right; + f32 right_z; Vec2 up; f32 up_z; u8* it = texform; + right.x = x0; + right.y = y0; + right_z = z0; up.x = x1; up.y = y1; up_z = z1; @@ -839,7 +840,8 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, setVtxDesc(3); GXBegin(GX_QUADS, GX_VTXFMT3, 4); } - GXPosition3f32(prev_x - x0, prev_y - y0, prev_z - z0); + GXPosition3f32(prev_x - right.x, prev_y - right.y, + prev_z - right_z); { u8 a = color.a; f32 alpha = (f32) a * pp->trail; @@ -862,7 +864,7 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; } - GXPosition3f32(x + x0, y + y0, z + z0); + GXPosition3f32(x + right.x, y + right.y, z + right_z); { u8 a = color.a; u8 b = color.b; @@ -942,9 +944,9 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & TexFlipT) { t = 1.0f - t; } - GXWGFifo.f32 = up.x * tx + (x0 * sx + x); - GXWGFifo.f32 = up.y * tx + (y0 * sx + y); - GXWGFifo.f32 = up_z * tx + (z0 * sx + z); + GXWGFifo.f32 = up.x * tx + (right.x * sx + x); + GXWGFifo.f32 = up.y * tx + (right.y * sx + y); + GXWGFifo.f32 = up_z * tx + (right_z * sx + z); GXColor4u8(color.r, color.g, color.b, alpha); if (pp->kind & DispTexture) { GXWGFifo.f32 = s; @@ -962,7 +964,7 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, setVtxDesc(1); GXBegin(GX_QUADS, GX_VTXFMT1, 4); } - GXPosition3f32(x - x0, y - y0, z - z0); + GXPosition3f32(x - right.x, y - right.y, z - right_z); if (pp->kind & DispTexture) { GXWGFifo.u8 = (pp->kind >> 16) & 0xC; } @@ -970,7 +972,7 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; } - GXPosition3f32(x + x0, y + y0, z + z0); + GXPosition3f32(x + right.x, y + right.y, z + right_z); if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; } @@ -1009,9 +1011,9 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & TexFlipT) { t = 1.0f - t; } - GXWGFifo.f32 = x + x0 * sx + up.x * tx; - GXWGFifo.f32 = y + y0 * sx + up.y * tx; - GXWGFifo.f32 = z + z0 * sx + up_z * tx; + GXWGFifo.f32 = x + right.x * sx + up.x * tx; + GXWGFifo.f32 = y + right.y * sx + up.y * tx; + GXWGFifo.f32 = z + right_z * sx + up_z * tx; if (pp->kind & DispTexture) { GXWGFifo.f32 = s; GXWGFifo.f32 = t; From f2f456c52d7e2b52f4bb6497bdbe684b8515742a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 01:19:00 -0700 Subject: [PATCH 37/79] psdisp: pass cached colors directly --- src/sysdolphin/baselib/psdisp.c | 48 ++++++--------------------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 4efd860ca2..95c7215441 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -697,24 +697,12 @@ static inline HSD_Particle* psDispSubPointTrail(HSD_Particle* pp) } for (i = count; i != 0; i--) { GXPosition3f32(p[1].x, p[1].y, p[1].z); - { - u8 a = c[1].a; - u8 b = c[1].b; - u8 g = c[1].g; - u8 r = c[1].r; - GXColor4u8(r, g, b, a); - } + GXColor4u8(c[1].r, c[1].g, c[1].b, c[1].a); if (pp->kind & DispTexture) { GXTexCoord1x8(0); } GXPosition3f32(p[0].x, p[0].y, p[0].z); - { - u8 a = c[0].a; - u8 b = c[0].b; - u8 g = c[0].g; - u8 r = c[0].r; - GXColor4u8(r, g, b, a); - } + GXColor4u8(c[0].r, c[0].g, c[0].b, c[0].a); if (pp->kind & DispTexture) { GXTexCoord1x8(1); } @@ -743,24 +731,12 @@ static inline HSD_Particle* psDispSubPointTrail(HSD_Particle* pp) } for (i = count; i != 0; i--) { GXPosition3f32(p[1].x, p[1].y, p[1].z); - { - u8 a = c[1].a; - u8 b = c[1].b; - u8 g = c[1].g; - u8 r = c[1].r; - GXColor4u8(r, g, b, a); - } + GXColor4u8(c[1].r, c[1].g, c[1].b, c[1].a); if (pp->kind & DispTexture) { GXTexCoord1x8(0); } GXPosition3f32(p[0].x, p[0].y, p[0].z); - { - u8 a = c[0].a; - u8 b = c[0].b; - u8 g = c[0].g; - u8 r = c[0].r; - GXColor4u8(r, g, b, a); - } + GXColor4u8(c[0].r, c[0].g, c[0].b, c[0].a); if (pp->kind & DispTexture) { GXTexCoord1x8(1); } @@ -1923,7 +1899,6 @@ void psDispParticles(u32 target_link, u32 sw) s32 needs_setup; void* sp79C; psdisp_Tlut tlut_obj; - GXColor chan_mat_color; GXTexObj sp764; HSD_Particle* sorted_particles; HSD_Particle* non_edge_particles; @@ -1931,9 +1906,6 @@ void psDispParticles(u32 target_link, u32 sw) GXTlutObj gx_tlut_obj; s32 alpha_compare_mode; s32 prev_tex_interp_near; - GXColor sp6D8; - GXColor sp6D4; - GXColor sp6D0; u32 prev_kind; HSD_Particle* pp; @@ -1997,20 +1969,16 @@ void psDispParticles(u32 target_link, u32 sw) prevChanMat.r = prevChanMat.g = prevChanMat.b = 0xFF; prevChanAmb.r = prevChanAmb.g = prevChanAmb.b = 0xFF; prevChanMat.a = prevChanAmb.a = 0xFF; - chan_mat_color = prevChanMat; - GXSetChanMatColor(GX_COLOR0A0, chan_mat_color); + GXSetChanMatColor(GX_COLOR0A0, prevChanMat); GXSetChanAmbColor(GX_COLOR0A0, prevChanAmb); psSetupTevInvalidState(); psSetupTevCommon(); psSetColor(&prevColorPrim, 0xFF); psSetColor(&prevColorEnv, 0); psSetColor(&prevColorMat, 0xFF); - sp6D8 = prevColorPrim; - GXSetTevColor(GX_TEVREG0, sp6D8); - sp6D4 = prevColorEnv; - GXSetTevColor(GX_TEVREG1, sp6D4); - sp6D0 = prevColorMat; - GXSetTevColor(GX_TEVREG2, sp6D0); + GXSetTevColor(GX_TEVREG0, prevColorPrim); + GXSetTevColor(GX_TEVREG1, prevColorEnv); + GXSetTevColor(GX_TEVREG2, prevColorMat); HSD_PSDisp_804D792C = -1; GXSetZCompLoc(GX_FALSE); HSD_CObjGetViewingMtx(HSD_CObjGetCurrent(), vmtx); From d1aa54e6ea7013f12680263dee8d2f350c68a408 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 01:20:48 -0700 Subject: [PATCH 38/79] psdisp: scalarize the previous AppSRT point --- src/sysdolphin/baselib/psdisp.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 95c7215441..4740f8a420 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1237,8 +1237,9 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) Mtx scratch_mtx; Vec3 scratch_scale; Vec3 cur_pos; - Vec3 prev_pos; - f32 ax; + f32 prev_x; + f32 prev_y; + f32 prev_z; s32 w; psSetCurrentMtx(GX_PNMTX1); @@ -1268,32 +1269,31 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) f32 z; calcTornadoLastPos(pp, &x, &y, &z); - prev_pos.x = + prev_x = pp->appsrt->x70 + (pp->appsrt->x6C * z + (pp->appsrt->ssx * x + pp->appsrt->ssy * y)); - prev_pos.y = + prev_y = pp->appsrt->x80 + (pp->appsrt->x7C * z + (pp->appsrt->x74 * x + pp->appsrt->x78 * y)); - prev_pos.z = + prev_z = pp->appsrt->x90 + (pp->appsrt->x8C * z + (pp->appsrt->x84 * x + pp->appsrt->x88 * y)); } else { f32 dy = pp->pos.y - pp->vel.y; f32 dx = pp->pos.x - pp->vel.x; f32 dz = pp->pos.z - pp->vel.z; - prev_pos.x = + prev_x = pp->appsrt->x70 + (pp->appsrt->x6C * dz + (pp->appsrt->ssx * dx + pp->appsrt->ssy * dy)); - prev_pos.y = + prev_y = pp->appsrt->x80 + (pp->appsrt->x7C * dz + (pp->appsrt->x78 * dy + pp->appsrt->x74 * dx)); - prev_pos.z = + prev_z = pp->appsrt->x90 + (pp->appsrt->x8C * dz + (pp->appsrt->x84 * dx + pp->appsrt->x88 * dy)); } - ax = pp->size > 42.5 ? 255.0f : 6.0f * pp->size; - w = (s32) ax; + w = (s32) ((pp->size > 42.5) ? 255.0f : 6.0f * pp->size); if (pp->kind & Trail) { GXColor draw_color; @@ -1309,9 +1309,9 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) setVtxDesc(3); GXBegin(GX_LINES, GX_VTXFMT3, 2); } - GXWGFifo.f32 = prev_pos.x; - GXWGFifo.f32 = prev_pos.y; - GXWGFifo.f32 = prev_pos.z; + GXWGFifo.f32 = prev_x; + GXWGFifo.f32 = prev_y; + GXWGFifo.f32 = prev_z; { u8 a = draw_color.a; f32 alpha = (f32) a * pp->trail; From 0437a254035f86c6da135b5f22497f9a7818cbf0 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 01:29:34 -0700 Subject: [PATCH 39/79] psdisp: pass AppSRT point colors directly --- src/sysdolphin/baselib/psdisp.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 4740f8a420..55075838d8 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1312,27 +1312,15 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) GXWGFifo.f32 = prev_x; GXWGFifo.f32 = prev_y; GXWGFifo.f32 = prev_z; - { - u8 a = draw_color.a; - f32 alpha = (f32) a * pp->trail; - u8 b = draw_color.b; - u8 g = draw_color.g; - u8 r = draw_color.r; - GXColor4u8(r, g, b, (u8) alpha); - } + GXColor4u8(draw_color.r, draw_color.g, draw_color.b, + (u8) ((f32) draw_color.a * pp->trail)); if (pp->kind & DispTexture) { GXTexCoord1x8(0); } GXWGFifo.f32 = cur_pos.x; GXWGFifo.f32 = cur_pos.y; GXWGFifo.f32 = cur_pos.z; - { - u8 a = draw_color.a; - u8 b = draw_color.b; - u8 g = draw_color.g; - u8 r = draw_color.r; - GXColor4u8(r, g, b, a); - } + GXColor4u8(draw_color.r, draw_color.g, draw_color.b, draw_color.a); if (pp->kind & DispTexture) { GXTexCoord1x8(1); } From 5857167ad6cabe84efb817b28ba15ac01685665f Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 01:31:45 -0700 Subject: [PATCH 40/79] psdisp: scalarize the current AppSRT point --- src/sysdolphin/baselib/psdisp.c | 40 +++++++++++++++++---------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 55075838d8..50b0a9cf09 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1236,7 +1236,9 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) { Mtx scratch_mtx; Vec3 scratch_scale; - Vec3 cur_pos; + f32 cur_x; + f32 cur_y; + f32 cur_z; f32 prev_x; f32 prev_y; f32 prev_z; @@ -1250,18 +1252,18 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) HSD_psAppSRT* appsrt = pp->appsrt; f32 y_component; - cur_pos.x = appsrt->ssy * (y_component = pp->pos.y); - cur_pos.y = appsrt->x78 * y_component; - cur_pos.z = appsrt->x88 * y_component; - cur_pos.x = appsrt->ssx * pp->pos.x + cur_pos.x; - cur_pos.z = (y_component = appsrt->x74 * pp->pos.x + cur_pos.y, - appsrt->x84 * pp->pos.x + cur_pos.z); - cur_pos.x = appsrt->x6C * pp->pos.z + cur_pos.x; - cur_pos.y = appsrt->x7C * pp->pos.z + y_component; - cur_pos.z = appsrt->x8C * pp->pos.z + cur_pos.z; - cur_pos.x = appsrt->x70 + cur_pos.x; - cur_pos.y = appsrt->x80 + cur_pos.y; - cur_pos.z = appsrt->x90 + cur_pos.z; + cur_x = appsrt->ssy * (y_component = pp->pos.y); + cur_y = appsrt->x78 * y_component; + cur_z = appsrt->x88 * y_component; + cur_x = appsrt->ssx * pp->pos.x + cur_x; + cur_z = (y_component = appsrt->x74 * pp->pos.x + cur_y, + appsrt->x84 * pp->pos.x + cur_z); + cur_z = (cur_x = appsrt->x6C * pp->pos.z + cur_x, + cur_y = appsrt->x7C * pp->pos.z + y_component, + appsrt->x8C * pp->pos.z + cur_z); + cur_x = appsrt->x70 + cur_x; + cur_y = appsrt->x80 + cur_y; + cur_z = appsrt->x90 + cur_z; } if (pp->kind & Tornado) { f32 x; @@ -1317,9 +1319,9 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) if (pp->kind & DispTexture) { GXTexCoord1x8(0); } - GXWGFifo.f32 = cur_pos.x; - GXWGFifo.f32 = cur_pos.y; - GXWGFifo.f32 = cur_pos.z; + GXWGFifo.f32 = cur_x; + GXWGFifo.f32 = cur_y; + GXWGFifo.f32 = cur_z; GXColor4u8(draw_color.r, draw_color.g, draw_color.b, draw_color.a); if (pp->kind & DispTexture) { GXTexCoord1x8(1); @@ -1336,9 +1338,9 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) setVtxDesc(1); GXBegin(GX_POINTS, GX_VTXFMT1, 1); } - GXWGFifo.f32 = cur_pos.x; - GXWGFifo.f32 = cur_pos.y; - GXWGFifo.f32 = cur_pos.z; + GXWGFifo.f32 = cur_x; + GXWGFifo.f32 = cur_y; + GXWGFifo.f32 = cur_z; if (pp->kind & DispTexture) { GXTexCoord1x8(1); } From 3ad55daf02cb315b42db0da1401d0c6b35971e78 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 01:38:44 -0700 Subject: [PATCH 41/79] psdisp: order polygon trail deltas --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 50b0a9cf09..c1ca1fa5f0 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -868,9 +868,9 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, f32 up_len = sqrtf(up.x * up.x + up.y * up.y + up_z * up_z); if (up_len != 0.0f) { + f32 dz = z - prev_z; f32 dx = x - prev_x; f32 dy = y - prev_y; - f32 dz = z - prev_z; f32 xl = dx * dx; f32 yl = dy * dy; f32 zl = dz * dz; From e8fded7065ea42531198d7da2a709cd4691d8ffe Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 11:10:30 -0700 Subject: [PATCH 42/79] psdisp: reserve the frame's missing spill slot psDispParticles builds a 0x998 frame where the DOL has 0x9a0: the retail compile spills two doubles in psDispSub's perspective block (0x888 and 0x890) and this source spills only one. A four-byte reserve rounds the frame back to 0x9a0, which realigns the constant pool and the whole callee-saved save area. Recovers 13 diff rows (693 -> 680); the reserve goes away once the second spill is induced for real. --- src/sysdolphin/baselib/psdisp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index c1ca1fa5f0..eafef34489 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1880,6 +1880,7 @@ static inline void psUpdateProjectionCache(f32 perspective) #endif void psDispParticles(u32 target_link, u32 sw) { + UNUSED u8 stack_pad[4]; s32 sp7B4; void* sp7B0; u32 sp7AC; From 60881e117368da7aa293aa068ddd140ef293af9b Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 11:42:31 -0700 Subject: [PATCH 43/79] psdisp: order the trail alpha and the non-billboard axes Two independent statement-order recoveries found by sweeping every adjacent pair of data-independent assignments in the file: - both trail loops compute `alpha` before `converted_alpha`, which places four stack temps on their retail slots (8 rows); - psDispSub's non-billboard branch assigns each `up_*` before its `right_*`, matching the branch's register order (3 rows). 693 -> 669 rows with the frame reserve; no instruction-stream rows either way. --- src/sysdolphin/baselib/psdisp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index eafef34489..4f60068179 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -908,8 +908,8 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, } t = *(f32*) &it[4]; it += 8; - converted_alpha = (s32) (255.0f - t * trail_alpha); alpha = (s32) (255.0f - t * trail_alpha); + converted_alpha = (s32) (255.0f - t * trail_alpha); if (converted_alpha < 0) { alpha = 0; } @@ -1030,12 +1030,12 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) right_z = rvmtx[2][0] * pp->size; up_z = -rvmtx[2][1] * pp->size; } else { - right_x = HSD_PSDisp_804D7914 * pp->size; up_x = HSD_PSDisp_804D7918 * pp->size; - right_y = HSD_PSDisp_804D791C * pp->size; + right_x = HSD_PSDisp_804D7914 * pp->size; up_y = HSD_PSDisp_804D7920 * pp->size; - right_z = HSD_PSDisp_804D7924 * pp->size; + right_y = HSD_PSDisp_804D791C * pp->size; up_z = HSD_PSDisp_804D7928 * pp->size; + right_z = HSD_PSDisp_804D7924 * pp->size; } if ((pp->kind & Trail) || (pp->kind & DirVec)) { f32 x; @@ -1690,8 +1690,8 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) } t = *(f32*) &it[4]; it += 8; - converted_alpha = (s32) (255.0f - t * trail_alpha); alpha = (s32) (255.0f - t * trail_alpha); + converted_alpha = (s32) (255.0f - t * trail_alpha); if (converted_alpha < 0) { alpha = 0; } From 79f7edee47d1b5e21ce313b6a5fad9bd5d062186 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 11:45:05 -0700 Subject: [PATCH 44/79] psdisp: give psUpdateBillboardAxes a local per axis The two reused `right`/`up` locals collapse the helper into two webs. The retail code births six, and because this helper is lowered before psDispSub, its web count sets the virtual-register numbering for everything downstream: splitting them rotates psDispSub's nine callee-saved float registers onto their retail assignment. 669 -> 661 rows, 279 -> 271 register rows, no instruction-stream rows. The same eight rows are reachable by swapping the reused pair instead, but that loads `inv_view[i][1]` before `inv_view[i][0]` and costs two real rows. --- src/sysdolphin/baselib/psdisp.c | 34 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 4f60068179..ca4bf97614 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1808,21 +1808,25 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) static inline void psUpdateBillboardAxes(const Mtx inv_view) { - f32 right; - f32 up; - - right = inv_view[0][0]; - up = inv_view[0][1]; - HSD_PSDisp_804D7914 = right + up; - HSD_PSDisp_804D7918 = right - up; - right = inv_view[1][0]; - up = inv_view[1][1]; - HSD_PSDisp_804D791C = right + up; - HSD_PSDisp_804D7920 = right - up; - right = inv_view[2][0]; - up = inv_view[2][1]; - HSD_PSDisp_804D7924 = right + up; - HSD_PSDisp_804D7928 = right - up; + f32 right_x; + f32 up_x; + f32 right_y; + f32 up_y; + f32 right_z; + f32 up_z; + + right_x = inv_view[0][0]; + up_x = inv_view[0][1]; + HSD_PSDisp_804D7914 = right_x + up_x; + HSD_PSDisp_804D7918 = right_x - up_x; + right_y = inv_view[1][0]; + up_y = inv_view[1][1]; + HSD_PSDisp_804D791C = right_y + up_y; + HSD_PSDisp_804D7920 = right_y - up_y; + right_z = inv_view[2][0]; + up_z = inv_view[2][1]; + HSD_PSDisp_804D7924 = right_z + up_z; + HSD_PSDisp_804D7928 = right_z - up_z; } static inline void psUpdateProjectionCache(f32 perspective) From a60bad139435f0bc2142b7018936cd5a83a33e1a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 13:00:35 -0700 Subject: [PATCH 45/79] psdisp: reorder psDispParticles' declaration band Hill-climbing every adjacent swap in the function-scope declaration list moves six objects onto retail slots. This band really is numbered by declaration order, unlike the inlined helpers' locals, and no previous sweep had touched it. 661 -> 642 rows, 382 -> 363 stack rows, no instruction-stream rows. The `spNNNN` placeholders are named for offsets they no longer all sit on; they want renaming once the band stops moving. --- src/sysdolphin/baselib/psdisp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index ca4bf97614..e5b88e4cc4 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1885,17 +1885,17 @@ static inline void psUpdateProjectionCache(f32 perspective) void psDispParticles(u32 target_link, u32 sw) { UNUSED u8 stack_pad[4]; - s32 sp7B4; void* sp7B0; - u32 sp7AC; + s32 sp7B4; u32 sp7A8; + u32 sp7AC; u8 sp7A5; u8 sp7A4; - s32 needs_setup; void* sp79C; + s32 needs_setup; psdisp_Tlut tlut_obj; - GXTexObj sp764; HSD_Particle* sorted_particles; + GXTexObj sp764; HSD_Particle* non_edge_particles; psdisp_Mtx billboard_mtx; GXTlutObj gx_tlut_obj; From 10fc7067a182d827887f69d5c62b343c128d808c Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 16:02:27 -0700 Subject: [PATCH 46/79] psdisp: permute psDispParticles' declaration band Adjacent swaps had converged at 642. Sweeping full permutations of every sliding window of three declarations reaches orders pairwise swaps cannot, and hill-climbing that to convergence takes another fifteen rows off the frame: 642 -> 634 -> 629 -> 627 (stack 363 -> 348) No register or instruction-stream rows either way. As before, the `spNNNN` placeholders are named for offsets they no longer sit on. --- src/sysdolphin/baselib/psdisp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index e5b88e4cc4..03d56dfdf7 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1885,18 +1885,18 @@ static inline void psUpdateProjectionCache(f32 perspective) void psDispParticles(u32 target_link, u32 sw) { UNUSED u8 stack_pad[4]; - void* sp7B0; - s32 sp7B4; u32 sp7A8; + s32 sp7B4; + void* sp7B0; u32 sp7AC; + void* sp79C; u8 sp7A5; u8 sp7A4; - void* sp79C; s32 needs_setup; psdisp_Tlut tlut_obj; - HSD_Particle* sorted_particles; - GXTexObj sp764; HSD_Particle* non_edge_particles; + GXTexObj sp764; + HSD_Particle* sorted_particles; psdisp_Mtx billboard_mtx; GXTlutObj gx_tlut_obj; s32 alpha_compare_mode; From 0850f018e7896085e0c8e891a4fecb7bf18596b7 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 17:08:56 -0700 Subject: [PATCH 47/79] psdisp: drop psDispSubMakePolygon's unused velocity parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The helper took fourteen arguments, three of which it never read — the body uses pp->vel.x/y/z directly. Passing three arguments nobody looks at is not something the original could plausibly have done; it is an artifact of reconstructing the call from the register-loaded arguments. Removing them is byte-identical (627 rows either way). Reading them instead of pp->vel is not: that costs 136 rows, which confirms the body really does go through the particle. --- src/sysdolphin/baselib/psdisp.c | 7 +++---- src/sysdolphin/baselib/psstructs.h | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 03d56dfdf7..42b0a3ba0c 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -776,8 +776,7 @@ static inline void psSetCurrentMtx(GXPosNrmMtx idx) } static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, - f32 y, f32 z, f32 ppvx, f32 ppvy, - f32 ppvz, f32 x0, f32 y0, f32 z0, + f32 y, f32 z, f32 x0, f32 y0, f32 z0, f32 x1, f32 y1, f32 z1) { f32 prev_x; @@ -1158,8 +1157,8 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) up_y = mtx[1][2] * uz + t2; up_z = mtx[2][2] * uz + t4; } - psDispSubMakePolygon(pp, texform, x, y, z, pp->vel.x, pp->vel.y, pp->vel.z, - right_x, right_y, right_z, up_x, up_y, up_z); + psDispSubMakePolygon(pp, texform, x, y, z, right_x, right_y, right_z, up_x, + up_y, up_z); } static inline void psScaleAppSRTAxes(HSD_Particle* pp, Mtx mtx) diff --git a/src/sysdolphin/baselib/psstructs.h b/src/sysdolphin/baselib/psstructs.h index 5d34df57b0..f5bf38635f 100644 --- a/src/sysdolphin/baselib/psstructs.h +++ b/src/sysdolphin/baselib/psstructs.h @@ -300,8 +300,7 @@ static inline void psSetCurrentMtx(GXPosNrmMtx idx); static inline HSD_Particle* psDispSubPoint(HSD_Particle* pp); static inline HSD_Particle* psDispSubPointTrail(HSD_Particle* pp); static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, - f32 y, f32 z, f32 ppvx, f32 ppvy, - f32 ppvz, f32 x0, f32 y0, f32 z0, + f32 y, f32 z, f32 x0, f32 y0, f32 z0, f32 x1, f32 y1, f32 z1); static inline void psDispSub(HSD_Particle* pp, u8* texform); From 71bd2eada4ed8023853ebe0d8c812f89c37bbc38 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 20:53:48 -0700 Subject: [PATCH 48/79] psdisp: name the current-x accumulator and guard the trail rescale psDispSub's projected-position block gains the fourth accumulator its sibling in psDispSubAppSRT already has, and names the repeated pvmtx[1][3] load. psDispSubMakePolygon only rescales the trail axis when there is a primitive to draw, which keeps the axis in one web across both emission loops instead of splitting it at the multiply. --- src/sysdolphin/baselib/psdisp.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 42b0a3ba0c..7eb13a7a2b 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -878,9 +878,11 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, u32 primitive_count = *(u32*) it; it += sizeof(u32); - up.x *= ratio; - up.y *= ratio; - up_z *= ratio; + if (primitive_count != 0) { + up.x *= ratio; + up.y *= ratio; + up_z *= ratio; + } for (; primitive_count != 0; primitive_count--) { GXPrimitive primitive = it[0]; u8 count = it[1]; @@ -1050,11 +1052,12 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) f32 w1inv; f32 cur_y_term; f32 prev_y_terms; - f32 cur_y; f32 prev_xy; f32 prev_x_sum; f32 cur_yx; f32 prev_yx; + f32 cur_x_sum; + f32 pv13; if (pp->kind & Tornado) { calcTornadoLastPos(pp, &prev_x, &prev_y, &prev_z); @@ -1078,16 +1081,16 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) w1inv = -1.0f / w1; prev_y_terms = pvmtx[1][1] * prev_y; prev_xy = pvmtx[0][1] * prev_y; - cur_y = pvmtx[0][1] * pp->pos.y; cur_y_term = pvmtx[1][1] * pp->pos.y; prev_x_sum = pvmtx[0][0] * prev_x + prev_xy; prev_yx = pvmtx[1][0] * prev_x + prev_y_terms; cur_yx = pvmtx[1][0] * pp->pos.x + cur_y_term; - x = w0inv * (pvmtx[0][3] + (pvmtx[0][2] * pp->pos.z + - (pvmtx[0][0] * pp->pos.x + cur_y))) - + cur_x_sum = pvmtx[0][0] * pp->pos.x + pvmtx[0][1] * pp->pos.y; + pv13 = pvmtx[1][3]; + x = w0inv * (pvmtx[0][3] + (pvmtx[0][2] * pp->pos.z + cur_x_sum)) - w1inv * (pvmtx[0][3] + (pvmtx[0][2] * prev_z + prev_x_sum)); - y = w0inv * (pvmtx[1][3] + (pvmtx[1][2] * pp->pos.z + cur_yx)) - - w1inv * (pvmtx[1][3] + (pvmtx[1][2] * prev_z + prev_yx)); + y = w0inv * (pv13 + (pvmtx[1][2] * pp->pos.z + cur_yx)) - + w1inv * (pv13 + (pvmtx[1][2] * prev_z + prev_yx)); } else if (pp->kind & Tornado) { f32 prev_x; f32 prev_y; @@ -1883,7 +1886,6 @@ static inline void psUpdateProjectionCache(f32 perspective) #endif void psDispParticles(u32 target_link, u32 sw) { - UNUSED u8 stack_pad[4]; u32 sp7A8; s32 sp7B4; void* sp7B0; From 6bb21c8cbdad84f10e76c26ccf077aec88fcb00b Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 21:21:46 -0700 Subject: [PATCH 49/79] psdisp: reorder psDispParticles' declaration band --- src/sysdolphin/baselib/psdisp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 7eb13a7a2b..e51dcaebf4 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1887,13 +1887,13 @@ static inline void psUpdateProjectionCache(f32 perspective) void psDispParticles(u32 target_link, u32 sw) { u32 sp7A8; - s32 sp7B4; void* sp7B0; u32 sp7AC; - void* sp79C; + s32 sp7B4; u8 sp7A5; u8 sp7A4; s32 needs_setup; + void* sp79C; psdisp_Tlut tlut_obj; HSD_Particle* non_edge_particles; GXTexObj sp764; From b1a6c3df7e9f2af86bb7380392ebed94c38ce97d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sat, 29 Aug 2026 23:37:47 -0700 Subject: [PATCH 50/79] psdisp: interleave the non-texform axis assignments like the texform arm --- src/sysdolphin/baselib/psdisp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index e51dcaebf4..b2a51c1561 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1031,12 +1031,12 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) right_z = rvmtx[2][0] * pp->size; up_z = -rvmtx[2][1] * pp->size; } else { - up_x = HSD_PSDisp_804D7918 * pp->size; right_x = HSD_PSDisp_804D7914 * pp->size; - up_y = HSD_PSDisp_804D7920 * pp->size; + up_x = HSD_PSDisp_804D7918 * pp->size; right_y = HSD_PSDisp_804D791C * pp->size; - up_z = HSD_PSDisp_804D7928 * pp->size; + up_y = HSD_PSDisp_804D7920 * pp->size; right_z = HSD_PSDisp_804D7924 * pp->size; + up_z = HSD_PSDisp_804D7928 * pp->size; } if ((pp->kind & Trail) || (pp->kind & DirVec)) { f32 x; From 9def51f46647f711e01f2420867df15ac2789a9d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 01:53:53 -0700 Subject: [PATCH 51/79] psdisp: order the projected-x and projected-y coefficients --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index b2a51c1561..0036780677 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1452,8 +1452,8 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) f32 f13; f32 f16; f32 f20; - f32 s808; f32 s804; + f32 s808; if (pp->kind & Tornado) { calcTornadoLastPos(pp, &prev_x, &prev_y, &prev_z); From 973493bcb19318c56b68ce49f00ec572016173c3 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 04:32:49 -0700 Subject: [PATCH 52/79] psdisp: order psDispSubAppSRT's previous-position deltas --- src/sysdolphin/baselib/psdisp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 0036780677..5ad83c45a4 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1403,9 +1403,9 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) draw_mtx[2][3] + (draw_mtx[2][2] * z + (draw_mtx[2][0] * x + draw_mtx[2][1] * y)); } else { - f32 dy = pp->pos.y - pp->vel.y; - f32 dx = pp->pos.x - pp->vel.x; f32 dz = pp->pos.z - pp->vel.z; + f32 dx = pp->pos.x - pp->vel.x; + f32 dy = pp->pos.y - pp->vel.y; prev_pos.x = draw_mtx[0][3] + (draw_mtx[0][2] * dz + From 5eea2ebe7f4b24220b985f141a25d1fd88a79995 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 06:51:57 -0700 Subject: [PATCH 53/79] psdisp: give psDispSubMakePolygon's trail colour function scope Row-neutral, but it takes the frame ordering nine objects closer to the DOL's (out-of-rank 31 -> 22, displacement 118 -> 90), and the surrounding previous-position scratch already lives at function scope despite only being used on the trail path. --- src/sysdolphin/baselib/psdisp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 5ad83c45a4..26a0a9fac7 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -779,6 +779,7 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, f32 y, f32 z, f32 x0, f32 y0, f32 z0, f32 x1, f32 y1, f32 z1) { + GXColor color; f32 prev_x; f32 prev_y; f32 prev_z; @@ -797,8 +798,6 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, psSetCurrentMtx(GX_PNMTX0); if (pp->kind & Trail) { - GXColor color; - if (pp->kind & Tornado) { calcTornadoLastPos(pp, &prev_x, &prev_y, &prev_z); } else { From a7070e6bd6f065d9d2a6910ccb5350ff1542b6de Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 09:06:41 -0700 Subject: [PATCH 54/79] psdisp: swap two of psDispParticles' scalar declarations --- src/sysdolphin/baselib/psdisp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 26a0a9fac7..4c06c20a12 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1885,10 +1885,10 @@ static inline void psUpdateProjectionCache(f32 perspective) #endif void psDispParticles(u32 target_link, u32 sw) { - u32 sp7A8; + s32 sp7B4; void* sp7B0; u32 sp7AC; - s32 sp7B4; + u32 sp7A8; u8 sp7A5; u8 sp7A4; s32 needs_setup; From 695a142fadc07b92c0ff8072887681058c33a9bb Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 10:08:38 -0700 Subject: [PATCH 55/79] psdisp: place non_edge_particles below the sorted list The DOL orders the frame non_edge_particles, sorted_particles, sp764 and leaves a four-byte hole where we had non_edge_particles; the last projected-y coefficient folds into its only use to pay for it. --- src/sysdolphin/baselib/psdisp.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 4c06c20a12..3d8e9a3fb3 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1845,7 +1845,6 @@ static inline void psUpdateProjectionCache(f32 perspective) f32 y0; f32 y1; f32 y2; - f32 y3; w0 = vmtx[2][0]; pvmtx[0][0] = x_scale * vmtx[0][0] + x_offset * w0; @@ -1860,11 +1859,10 @@ static inline void psUpdateProjectionCache(f32 perspective) y0 = y_offset * w0; y1 = y_offset * w1; y2 = y_offset * w2; - y3 = y_offset * w3; pvmtx[1][0] = y_scale * vmtx[1][0] + y0; pvmtx[1][1] = y_scale * vmtx[1][1] + y1; pvmtx[1][2] = y_scale * vmtx[1][2] + y2; - pvmtx[1][3] = y_scale * vmtx[1][3] + y3; + pvmtx[1][3] = y_scale * vmtx[1][3] + (y_offset * w3); } else { pvmtx[0][0] = prj[1] * vmtx[0][0] + prj[2]; pvmtx[0][1] = prj[1] * vmtx[0][1] + prj[2]; @@ -1894,9 +1892,10 @@ void psDispParticles(u32 target_link, u32 sw) s32 needs_setup; void* sp79C; psdisp_Tlut tlut_obj; - HSD_Particle* non_edge_particles; + UNUSED s32 stack_pad; GXTexObj sp764; HSD_Particle* sorted_particles; + HSD_Particle* non_edge_particles; psdisp_Mtx billboard_mtx; GXTlutObj gx_tlut_obj; s32 alpha_compare_mode; From 86d19c8d783078c3c7dcc587da44bce8884632cc Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 11:27:52 -0700 Subject: [PATCH 56/79] psdisp: order psDispSubAPPSRTPoint's projected-y products like its siblings --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 3d8e9a3fb3..b4c3fe53f9 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1290,7 +1290,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) (pp->appsrt->ssx * dx + pp->appsrt->ssy * dy)); prev_y = pp->appsrt->x80 + (pp->appsrt->x7C * dz + - (pp->appsrt->x78 * dy + pp->appsrt->x74 * dx)); + (pp->appsrt->x74 * dx + pp->appsrt->x78 * dy)); prev_z = pp->appsrt->x90 + (pp->appsrt->x8C * dz + (pp->appsrt->x84 * dx + pp->appsrt->x88 * dy)); From 7e40fd49e8c5315cc0085c1039c13d4fd3fe14a6 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 13:19:22 -0700 Subject: [PATCH 57/79] psdisp: keep projection cache locals in the dispatcher --- src/sysdolphin/baselib/psdisp.c | 101 +++++++++++++++++--------------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index b4c3fe53f9..c82493ba60 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1830,53 +1830,6 @@ static inline void psUpdateBillboardAxes(const Mtx inv_view) HSD_PSDisp_804D7928 = right_z - up_z; } -static inline void psUpdateProjectionCache(f32 perspective) -{ - GXGetProjectionv(prj); - if (perspective == prj[0]) { - f32 w0; - f32 x_scale = prj[1]; - f32 x_offset = prj[2]; - f32 w1; - f32 w2; - f32 y_scale; - f32 w3; - f32 y_offset; - f32 y0; - f32 y1; - f32 y2; - - w0 = vmtx[2][0]; - pvmtx[0][0] = x_scale * vmtx[0][0] + x_offset * w0; - w1 = vmtx[2][1]; - pvmtx[0][1] = x_scale * vmtx[0][1] + x_offset * w1; - w2 = vmtx[2][2]; - pvmtx[0][2] = x_scale * vmtx[0][2] + x_offset * w2; - w3 = vmtx[2][3]; - pvmtx[0][3] = x_scale * vmtx[0][3] + x_offset * w3; - y_scale = prj[3]; - y_offset = prj[4]; - y0 = y_offset * w0; - y1 = y_offset * w1; - y2 = y_offset * w2; - pvmtx[1][0] = y_scale * vmtx[1][0] + y0; - pvmtx[1][1] = y_scale * vmtx[1][1] + y1; - pvmtx[1][2] = y_scale * vmtx[1][2] + y2; - pvmtx[1][3] = y_scale * vmtx[1][3] + (y_offset * w3); - } else { - pvmtx[0][0] = prj[1] * vmtx[0][0] + prj[2]; - pvmtx[0][1] = prj[1] * vmtx[0][1] + prj[2]; - pvmtx[0][2] = prj[1] * vmtx[0][2] + prj[2]; - pvmtx[0][3] = prj[1] * vmtx[0][3] + prj[2]; - pvmtx[1][0] = prj[3] * vmtx[1][0] + prj[4]; - pvmtx[1][1] = prj[3] * vmtx[1][1] + prj[4]; - pvmtx[1][2] = prj[3] * vmtx[1][2] + prj[4]; - pvmtx[1][3] = prj[3] * vmtx[1][3] + prj[4]; - } - /// @todo Passing @c rvmtx directly swaps the first two axis loads. - psUpdateBillboardAxes(*(const Mtx*) rvmtx); -} - #ifdef MUST_MATCH #pragma push #pragma inline_depth(3) @@ -1977,7 +1930,59 @@ void psDispParticles(u32 target_link, u32 sw) GXSetZCompLoc(GX_FALSE); HSD_CObjGetViewingMtx(HSD_CObjGetCurrent(), vmtx); PSMTXInverse(vmtx, rvmtx); - psUpdateProjectionCache(0.0f); + { + f32 w0; + f32 x_scale; + f32 x_offset; + f32 w1; + f32 w2; + f32 y_scale; + f32 w3; + f32 y_offset; + f32 y0; + f32 y1; + f32 y2; + + GXGetProjectionv(prj); + if (0.0f == prj[0]) { + x_scale = prj[1]; + x_offset = prj[2]; + w0 = vmtx[2][0]; + pvmtx[0][0] = + x_scale * vmtx[0][0] + x_offset * w0; + w1 = vmtx[2][1]; + pvmtx[0][1] = + x_scale * vmtx[0][1] + x_offset * w1; + w2 = vmtx[2][2]; + pvmtx[0][2] = + x_scale * vmtx[0][2] + x_offset * w2; + w3 = vmtx[2][3]; + pvmtx[0][3] = + x_scale * vmtx[0][3] + x_offset * w3; + y_scale = prj[3]; + y_offset = prj[4]; + y0 = y_offset * w0; + y1 = y_offset * w1; + y2 = y_offset * w2; + pvmtx[1][0] = y_scale * vmtx[1][0] + y0; + pvmtx[1][1] = y_scale * vmtx[1][1] + y1; + pvmtx[1][2] = y_scale * vmtx[1][2] + y2; + pvmtx[1][3] = + y_scale * vmtx[1][3] + (y_offset * w3); + } else { + pvmtx[0][0] = prj[1] * vmtx[0][0] + prj[2]; + pvmtx[0][1] = prj[1] * vmtx[0][1] + prj[2]; + pvmtx[0][2] = prj[1] * vmtx[0][2] + prj[2]; + pvmtx[0][3] = prj[1] * vmtx[0][3] + prj[2]; + pvmtx[1][0] = prj[3] * vmtx[1][0] + prj[4]; + pvmtx[1][1] = prj[3] * vmtx[1][1] + prj[4]; + pvmtx[1][2] = prj[3] * vmtx[1][2] + prj[4]; + pvmtx[1][3] = prj[3] * vmtx[1][3] + prj[4]; + } + /// @todo Passing @c rvmtx directly swaps the first + /// two axis loads. + psUpdateBillboardAxes(*(const Mtx*) rvmtx); + } GXLoadPosMtxImm(vmtx, GX_PNMTX0); billboard_mtx = HSD_PSDisp_803B9628; GXLoadPosMtxImm(billboard_mtx.mtx, GX_PNMTX1); From be55e5acec4bc742bbd560e4034ab0f249d8e88c Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 13:25:41 -0700 Subject: [PATCH 58/79] psdisp: scope the texture matrix to mirror updates --- src/sysdolphin/baselib/psdisp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index c82493ba60..d9981d165e 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1888,7 +1888,6 @@ void psDispParticles(u32 target_link, u32 sw) u32 blend_mode; u8 alpha0; u8 alpha1; - Mtx temp_mtx; GXTexWrapMode wrap_s; GXTexWrapMode wrap_t; f32 scale_s; @@ -2080,6 +2079,8 @@ void psDispParticles(u32 target_link, u32 sw) wrap_t = GX_CLAMP; } if ((pp->kind & (MirrorS | MirrorT)) != sp7A8) { + Mtx temp_mtx; + sp7A8 = pp->kind & (MirrorS | MirrorT); sp7B0 = NULL; PSMTXScale(temp_mtx, scale_s, scale_t, 1.0f); From 37c7fc70d2ecf876356658bb20de30b70b1114ae Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 14:20:53 -0700 Subject: [PATCH 59/79] psdisp: group the projected Y coefficients --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index d9981d165e..67fc3ad49c 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1936,8 +1936,8 @@ void psDispParticles(u32 target_link, u32 sw) f32 w1; f32 w2; f32 y_scale; - f32 w3; f32 y_offset; + f32 w3; f32 y0; f32 y1; f32 y2; From 7f4df353f6c57194fd5fce56d7bb0bdc71bbd47e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 14:23:32 -0700 Subject: [PATCH 60/79] psdisp: reorder projection cache lifetimes --- src/sysdolphin/baselib/psdisp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 67fc3ad49c..ebdbb6717e 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1934,9 +1934,9 @@ void psDispParticles(u32 target_link, u32 sw) f32 x_scale; f32 x_offset; f32 w1; - f32 w2; - f32 y_scale; f32 y_offset; + f32 y_scale; + f32 w2; f32 w3; f32 y0; f32 y1; From d6cfe9e16096e8ac5c4fe801909bbb7cf3afe701 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 14:25:24 -0700 Subject: [PATCH 61/79] psdisp: align projection coefficient lifetimes --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index ebdbb6717e..a6edfe2c21 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1931,8 +1931,8 @@ void psDispParticles(u32 target_link, u32 sw) PSMTXInverse(vmtx, rvmtx); { f32 w0; - f32 x_scale; f32 x_offset; + f32 x_scale; f32 w1; f32 y_offset; f32 y_scale; From a955586c030c646c02f2704ed72b329deb8049ea Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 18:07:47 -0700 Subject: [PATCH 62/79] psdisp: stage the projected x products --- src/sysdolphin/baselib/psdisp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index a6edfe2c21..871ad10afd 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1078,13 +1078,14 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) return; } w1inv = -1.0f / w1; - prev_y_terms = pvmtx[1][1] * prev_y; prev_xy = pvmtx[0][1] * prev_y; + prev_y_terms = pvmtx[1][1] * prev_y; + cur_x_sum = pvmtx[0][1] * pp->pos.y; cur_y_term = pvmtx[1][1] * pp->pos.y; prev_x_sum = pvmtx[0][0] * prev_x + prev_xy; prev_yx = pvmtx[1][0] * prev_x + prev_y_terms; cur_yx = pvmtx[1][0] * pp->pos.x + cur_y_term; - cur_x_sum = pvmtx[0][0] * pp->pos.x + pvmtx[0][1] * pp->pos.y; + cur_x_sum = pvmtx[0][0] * pp->pos.x + cur_x_sum; pv13 = pvmtx[1][3]; x = w0inv * (pvmtx[0][3] + (pvmtx[0][2] * pp->pos.z + cur_x_sum)) - w1inv * (pvmtx[0][3] + (pvmtx[0][2] * prev_z + prev_x_sum)); From 670bf4df43ffe9dddd973cf2ae723766a912e741 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 18:17:22 -0700 Subject: [PATCH 63/79] psdisp: reserve the texture frame gap --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 871ad10afd..59ae3dbb9c 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1846,7 +1846,7 @@ void psDispParticles(u32 target_link, u32 sw) s32 needs_setup; void* sp79C; psdisp_Tlut tlut_obj; - UNUSED s32 stack_pad; + UNUSED s64 stack_pad; GXTexObj sp764; HSD_Particle* sorted_particles; HSD_Particle* non_edge_particles; From 2bf4fa627695a6704af3bf0c2d80be3ac427abd8 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 18:48:00 -0700 Subject: [PATCH 64/79] psdisp: separate the projected x product --- src/sysdolphin/baselib/psdisp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 59ae3dbb9c..e90d32fd82 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1055,6 +1055,7 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) f32 prev_x_sum; f32 cur_yx; f32 prev_yx; + f32 cur_x_product; f32 cur_x_sum; f32 pv13; @@ -1080,7 +1081,8 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) w1inv = -1.0f / w1; prev_xy = pvmtx[0][1] * prev_y; prev_y_terms = pvmtx[1][1] * prev_y; - cur_x_sum = pvmtx[0][1] * pp->pos.y; + cur_x_product = pvmtx[0][1] * pp->pos.y; + cur_x_sum = cur_x_product; cur_y_term = pvmtx[1][1] * pp->pos.y; prev_x_sum = pvmtx[0][0] * prev_x + prev_xy; prev_yx = pvmtx[1][0] * prev_x + prev_y_terms; @@ -1846,7 +1848,7 @@ void psDispParticles(u32 target_link, u32 sw) s32 needs_setup; void* sp79C; psdisp_Tlut tlut_obj; - UNUSED s64 stack_pad; + UNUSED s32 stack_pad; GXTexObj sp764; HSD_Particle* sorted_particles; HSD_Particle* non_edge_particles; From 42bec1daf224adb139bf3ed993fa6b494c5ed11c Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 19:11:14 -0700 Subject: [PATCH 65/79] psdisp: express the projected x accumulation directly --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index e90d32fd82..6e65141289 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1087,7 +1087,7 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) prev_x_sum = pvmtx[0][0] * prev_x + prev_xy; prev_yx = pvmtx[1][0] * prev_x + prev_y_terms; cur_yx = pvmtx[1][0] * pp->pos.x + cur_y_term; - cur_x_sum = pvmtx[0][0] * pp->pos.x + cur_x_sum; + cur_x_sum += pvmtx[0][0] * pp->pos.x; pv13 = pvmtx[1][3]; x = w0inv * (pvmtx[0][3] + (pvmtx[0][2] * pp->pos.z + cur_x_sum)) - w1inv * (pvmtx[0][3] + (pvmtx[0][2] * prev_z + prev_x_sum)); From 0f60972b7b86878400cc92e43fcd3cbe2a430849 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Sun, 30 Aug 2026 21:43:08 -0700 Subject: [PATCH 66/79] psdisp: lead the previous depth product with its position MWCC encodes the multiply's operand registers in source order; leading with prev_z recovers the retail register for the previous perspective depth term. --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 6e65141289..cd7496bf72 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1073,7 +1073,7 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) return; } w0inv = -1.0f / w0; - w1 = vmtx[2][3] + (vmtx[2][2] * prev_z + + w1 = vmtx[2][3] + (prev_z * vmtx[2][2] + (vmtx[2][0] * prev_x + vmtx[2][1] * prev_y)); if (0.0f == w1) { return; From 417e2dc05b7f771275e860b41bdf4452610dc66e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 01:46:03 -0700 Subject: [PATCH 67/79] psdisp: pair the previous perspective W products in retail order The inner sum of the previous position's W term rounds its Y product first, so the fused multiply-add carries the X product. Restoring that association and dropping the staged current-X product removes one wrong instruction and halves the local insertions. --- src/sysdolphin/baselib/psdisp.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index cd7496bf72..d60a65238d 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1055,7 +1055,6 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) f32 prev_x_sum; f32 cur_yx; f32 prev_yx; - f32 cur_x_product; f32 cur_x_sum; f32 pv13; @@ -1074,20 +1073,18 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) } w0inv = -1.0f / w0; w1 = vmtx[2][3] + (prev_z * vmtx[2][2] + - (vmtx[2][0] * prev_x + vmtx[2][1] * prev_y)); + (vmtx[2][1] * prev_y + vmtx[2][0] * prev_x)); if (0.0f == w1) { return; } w1inv = -1.0f / w1; prev_xy = pvmtx[0][1] * prev_y; prev_y_terms = pvmtx[1][1] * prev_y; - cur_x_product = pvmtx[0][1] * pp->pos.y; - cur_x_sum = cur_x_product; cur_y_term = pvmtx[1][1] * pp->pos.y; prev_x_sum = pvmtx[0][0] * prev_x + prev_xy; prev_yx = pvmtx[1][0] * prev_x + prev_y_terms; cur_yx = pvmtx[1][0] * pp->pos.x + cur_y_term; - cur_x_sum += pvmtx[0][0] * pp->pos.x; + cur_x_sum = pvmtx[0][0] * pp->pos.x + pvmtx[0][1] * pp->pos.y; pv13 = pvmtx[1][3]; x = w0inv * (pvmtx[0][3] + (pvmtx[0][2] * pp->pos.z + cur_x_sum)) - w1inv * (pvmtx[0][3] + (pvmtx[0][2] * prev_z + prev_x_sum)); From a84ada68cc52be39429134545da3ce8621751c30 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 01:50:42 -0700 Subject: [PATCH 68/79] psdisp: restore the matrix-first depth product in the previous W term The product-pair ordering committed alongside it subsumes the operand swap, so the term can read like its current-position sibling again at no cost. --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index d60a65238d..687e040981 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1072,7 +1072,7 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) return; } w0inv = -1.0f / w0; - w1 = vmtx[2][3] + (prev_z * vmtx[2][2] + + w1 = vmtx[2][3] + (vmtx[2][2] * prev_z + (vmtx[2][1] * prev_y + vmtx[2][0] * prev_x)); if (0.0f == w1) { return; From 1b33450a6084bf7a489b4ded748681676c2399fe Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 01:57:49 -0700 Subject: [PATCH 69/79] psdisp: name the projected X translation beside its Y sibling Naming both perspective translations, X before Y, gives the retail's load order for the pair and makes every emitted instruction in the function correct; only two positional insertions remain. --- src/sysdolphin/baselib/psdisp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 687e040981..33b80d3c93 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1057,6 +1057,7 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) f32 prev_yx; f32 cur_x_sum; f32 pv13; + f32 pv03; if (pp->kind & Tornado) { calcTornadoLastPos(pp, &prev_x, &prev_y, &prev_z); @@ -1085,9 +1086,10 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) prev_yx = pvmtx[1][0] * prev_x + prev_y_terms; cur_yx = pvmtx[1][0] * pp->pos.x + cur_y_term; cur_x_sum = pvmtx[0][0] * pp->pos.x + pvmtx[0][1] * pp->pos.y; + pv03 = pvmtx[0][3]; pv13 = pvmtx[1][3]; - x = w0inv * (pvmtx[0][3] + (pvmtx[0][2] * pp->pos.z + cur_x_sum)) - - w1inv * (pvmtx[0][3] + (pvmtx[0][2] * prev_z + prev_x_sum)); + x = w0inv * (pv03 + (pvmtx[0][2] * pp->pos.z + cur_x_sum)) - + w1inv * (pv03 + (pvmtx[0][2] * prev_z + prev_x_sum)); y = w0inv * (pv13 + (pvmtx[1][2] * pp->pos.z + cur_yx)) - w1inv * (pv13 + (pvmtx[1][2] * prev_z + prev_yx)); } else if (pp->kind & Tornado) { From 251e8a408359b959bc3c19243a06c89afe1d7d95 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 08:15:46 -0700 Subject: [PATCH 70/79] psdisp: home the third projected Y term at function scope gx_tlut_obj sits one word lower in the target, with a spare word between it and billboard_mtx. Declaring y2 at function scope supplies that word and drops the projection block's own count by one, leaving every other displacement where it was. --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 33b80d3c93..bfa1165803 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1852,6 +1852,7 @@ void psDispParticles(u32 target_link, u32 sw) HSD_Particle* sorted_particles; HSD_Particle* non_edge_particles; psdisp_Mtx billboard_mtx; + f32 y2; GXTlutObj gx_tlut_obj; s32 alpha_compare_mode; s32 prev_tex_interp_near; @@ -1942,7 +1943,6 @@ void psDispParticles(u32 target_link, u32 sw) f32 w3; f32 y0; f32 y1; - f32 y2; GXGetProjectionv(prj); if (0.0f == prj[0]) { From d794a11ef7bcd5e939b11aee7f156ed7ff39b6bb Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 08:51:57 -0700 Subject: [PATCH 71/79] psdisp: emit positions through GXPosition3f32 and drop the corner temporaries Six raw three-word GXWGFifo runs become GXPosition3f32 calls, in psDispSubPoint, psDispSubMakePolygon's textured loop and psDispSubAPPSRTPoint. Each bills three parameter homes into the shared inline-expansion band, which sits below both psDispSub's and psDispSubAppSRT's own locals. That band has to grow by exactly what psDispSubAppSRT's frame sheds or the whole layout moves, so the trail corner emissions give up their vx/vy temporaries and the alpha and segment length intermediates go with them. The two halves cancel to the byte and psDispSubAppSRT's locals land on their target displacements with the rest of the frame unmoved. --- src/sysdolphin/baselib/psdisp.c | 112 ++++++++++---------------------- 1 file changed, 34 insertions(+), 78 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index bfa1165803..7bd92e36a2 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -570,9 +570,7 @@ static inline HSD_Particle* psDispSubPoint(HSD_Particle* pp) f32 y = p->y; f32 x = p->x; p++; - GXWGFifo.f32 = x; - GXWGFifo.f32 = y; - GXWGFifo.f32 = z; + GXPosition3f32(x, y, z); if (pp->kind & DispTexture) { GXTexCoord1x8(1); } @@ -600,9 +598,7 @@ static inline HSD_Particle* psDispSubPoint(HSD_Particle* pp) f32 y = p->y; f32 x = p->x; p++; - GXWGFifo.f32 = x; - GXWGFifo.f32 = y; - GXWGFifo.f32 = z; + GXPosition3f32(x, y, z); if (pp->kind & DispTexture) { GXTexCoord1x8(1); } @@ -987,9 +983,9 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, if (pp->kind & TexFlipT) { t = 1.0f - t; } - GXWGFifo.f32 = x + right.x * sx + up.x * tx; - GXWGFifo.f32 = y + right.y * sx + up.y * tx; - GXWGFifo.f32 = z + right_z * sx + up_z * tx; + GXPosition3f32(x + right.x * sx + up.x * tx, + y + right.y * sx + up.y * tx, + z + right_z * sx + up_z * tx); if (pp->kind & DispTexture) { GXWGFifo.f32 = s; GXWGFifo.f32 = t; @@ -1314,17 +1310,13 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) setVtxDesc(3); GXBegin(GX_LINES, GX_VTXFMT3, 2); } - GXWGFifo.f32 = prev_x; - GXWGFifo.f32 = prev_y; - GXWGFifo.f32 = prev_z; + GXPosition3f32(prev_x, prev_y, prev_z); GXColor4u8(draw_color.r, draw_color.g, draw_color.b, (u8) ((f32) draw_color.a * pp->trail)); if (pp->kind & DispTexture) { GXTexCoord1x8(0); } - GXWGFifo.f32 = cur_x; - GXWGFifo.f32 = cur_y; - GXWGFifo.f32 = cur_z; + GXPosition3f32(cur_x, cur_y, cur_z); GXColor4u8(draw_color.r, draw_color.g, draw_color.b, draw_color.a); if (pp->kind & DispTexture) { GXTexCoord1x8(1); @@ -1341,9 +1333,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) setVtxDesc(1); GXBegin(GX_POINTS, GX_VTXFMT1, 1); } - GXWGFifo.f32 = cur_x; - GXWGFifo.f32 = cur_y; - GXWGFifo.f32 = cur_z; + GXPosition3f32(cur_x, cur_y, cur_z); if (pp->kind & DispTexture) { GXTexCoord1x8(1); } @@ -1579,16 +1569,11 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) setVtxDesc(3); GXBegin(GX_QUADS, GX_VTXFMT3, 4U); } + GXWGFifo.f32 = -ax + prev_pos.x; + GXWGFifo.f32 = -ay + prev_pos.y; + GXWGFifo.f32 = prev_pos.z; { - f32 vx = -ax + prev_pos.x; - f32 vy = -ay + prev_pos.y; - GXWGFifo.f32 = vx; - GXWGFifo.f32 = vy; - GXWGFifo.f32 = prev_pos.z; - } - { - u8 a = draw_color.a; - f32 alpha = (f32) a * pp->trail; + f32 alpha = (f32) draw_color.a * pp->trail; u8 b = draw_color.b; u8 g = draw_color.g; u8 r = draw_color.r; @@ -1597,13 +1582,9 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) if (pp->kind & DispTexture) { GXWGFifo.u8 = (pp->kind >> 16) & 0xC; } - { - f32 vx = -bx + cur_pos.x; - f32 vy = -by + cur_pos.y; - GXWGFifo.f32 = vx; - GXWGFifo.f32 = vy; - GXWGFifo.f32 = cur_pos.z; - } + GXWGFifo.f32 = -bx + cur_pos.x; + GXWGFifo.f32 = -by + cur_pos.y; + GXWGFifo.f32 = cur_pos.z; { u8 a = draw_color.a; u8 b = draw_color.b; @@ -1614,13 +1595,9 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; } - { - f32 vx = ax + cur_pos.x; - f32 vy = ay + cur_pos.y; - GXWGFifo.f32 = vx; - GXWGFifo.f32 = vy; - GXWGFifo.f32 = cur_pos.z; - } + GXWGFifo.f32 = ax + cur_pos.x; + GXWGFifo.f32 = ay + cur_pos.y; + GXWGFifo.f32 = cur_pos.z; { u8 a = draw_color.a; u8 b = draw_color.b; @@ -1631,13 +1608,9 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; } - { - f32 vx = bx + prev_pos.x; - f32 vy = by + prev_pos.y; - GXWGFifo.f32 = vx; - GXWGFifo.f32 = vy; - GXWGFifo.f32 = prev_pos.z; - } + GXWGFifo.f32 = bx + prev_pos.x; + GXWGFifo.f32 = by + prev_pos.y; + GXWGFifo.f32 = prev_pos.z; { u8 a = draw_color.a; f32 alpha = (f32) a * pp->trail; @@ -1660,8 +1633,7 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) f32 xl = dx * dx; f32 yl = dy * dy; f32 zl = dz * dz; - f32 segment_len = sqrtf(zl + (xl + yl)); - f32 ratio = segment_len / axis_len; + f32 ratio = sqrtf(zl + (xl + yl)) / axis_len; u32 primitive_count = *(u32*) it; bx *= ratio; @@ -1726,43 +1698,27 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) setVtxDesc(1); GXBegin(GX_QUADS, GX_VTXFMT1, 4U); } - { - f32 vx = -ax + cur_pos.x; - f32 vy = -ay + cur_pos.y; - GXWGFifo.f32 = vx; - GXWGFifo.f32 = vy; - GXWGFifo.f32 = cur_pos.z; - } + GXWGFifo.f32 = -ax + cur_pos.x; + GXWGFifo.f32 = -ay + cur_pos.y; + GXWGFifo.f32 = cur_pos.z; if (pp->kind & DispTexture) { GXWGFifo.u8 = (pp->kind >> 16) & 0xC; } - { - f32 vx = -bx + cur_pos.x; - f32 vy = -by + cur_pos.y; - GXWGFifo.f32 = vx; - GXWGFifo.f32 = vy; - GXWGFifo.f32 = cur_pos.z; - } + GXWGFifo.f32 = -bx + cur_pos.x; + GXWGFifo.f32 = -by + cur_pos.y; + GXWGFifo.f32 = cur_pos.z; if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 1; } - { - f32 vx = ax + cur_pos.x; - f32 vy = ay + cur_pos.y; - GXWGFifo.f32 = vx; - GXWGFifo.f32 = vy; - GXWGFifo.f32 = cur_pos.z; - } + GXWGFifo.f32 = ax + cur_pos.x; + GXWGFifo.f32 = ay + cur_pos.y; + GXWGFifo.f32 = cur_pos.z; if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; } - { - f32 vx = bx + cur_pos.x; - f32 vy = by + cur_pos.y; - GXWGFifo.f32 = vx; - GXWGFifo.f32 = vy; - GXWGFifo.f32 = cur_pos.z; - } + GXWGFifo.f32 = bx + cur_pos.x; + GXWGFifo.f32 = by + cur_pos.y; + GXWGFifo.f32 = cur_pos.z; if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 3; } From d0a7c1a0098a0b7af01a0a21a7433312b622f22c Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 09:37:08 -0700 Subject: [PATCH 72/79] psdisp: name the first emitted texture byte and fold the current X sum Deleting psDispSub's cur_x_sum intermediate frees a homed word and, unlike the rest of that family, also settles two register rows. Naming the texture byte at the first emission puts the word back one frame position lower, so nothing above moves. --- src/sysdolphin/baselib/psdisp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 7bd92e36a2..16faf3fab3 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -821,7 +821,9 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, GXColor4u8(r, g, b, (u8) alpha); } if (pp->kind & DispTexture) { - GXWGFifo.u8 = (pp->kind >> 16) & 0xC; + u8 tex_base = (pp->kind >> 16) & 0xC; + + GXWGFifo.u8 = tex_base; } GXPosition3f32(x - up.x, y - up.y, z - up_z); { @@ -1051,7 +1053,6 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) f32 prev_x_sum; f32 cur_yx; f32 prev_yx; - f32 cur_x_sum; f32 pv13; f32 pv03; @@ -1081,10 +1082,11 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) prev_x_sum = pvmtx[0][0] * prev_x + prev_xy; prev_yx = pvmtx[1][0] * prev_x + prev_y_terms; cur_yx = pvmtx[1][0] * pp->pos.x + cur_y_term; - cur_x_sum = pvmtx[0][0] * pp->pos.x + pvmtx[0][1] * pp->pos.y; pv03 = pvmtx[0][3]; pv13 = pvmtx[1][3]; - x = w0inv * (pv03 + (pvmtx[0][2] * pp->pos.z + cur_x_sum)) - + x = w0inv * (pv03 + (pvmtx[0][2] * pp->pos.z + + (pvmtx[0][0] * pp->pos.x + + pvmtx[0][1] * pp->pos.y))) - w1inv * (pv03 + (pvmtx[0][2] * prev_z + prev_x_sum)); y = w0inv * (pv13 + (pvmtx[1][2] * pp->pos.z + cur_yx)) - w1inv * (pv13 + (pvmtx[1][2] * prev_z + prev_yx)); From 838c83c15b05e579dccd35d0830a93c785262f64 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 09:57:56 -0700 Subject: [PATCH 73/79] psdisp: fold the projection cross terms and emit two more trail corners Four of psDispSub's projection intermediates and psDispSubMakePolygon's first trail alpha give up their homed words; two more of psDispSubAppSRT's corner emissions become GXPosition3f32 calls and bill the same total back into the shared expansion band. Balanced to the byte, so only psDispSub's own block moves. --- src/sysdolphin/baselib/psdisp.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 16faf3fab3..6249d977a6 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -814,11 +814,10 @@ static inline void psDispSubMakePolygon(HSD_Particle* pp, u8* texform, f32 x, prev_z - right_z); { u8 a = color.a; - f32 alpha = (f32) a * pp->trail; u8 b = color.b; u8 g = color.g; u8 r = color.r; - GXColor4u8(r, g, b, (u8) alpha); + GXColor4u8(r, g, b, (u8) ((f32) a * pp->trail)); } if (pp->kind & DispTexture) { u8 tex_base = (pp->kind >> 16) & 0xC; @@ -1047,12 +1046,8 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) f32 w0inv; f32 w1; f32 w1inv; - f32 cur_y_term; f32 prev_y_terms; - f32 prev_xy; f32 prev_x_sum; - f32 cur_yx; - f32 prev_yx; f32 pv13; f32 pv03; @@ -1076,20 +1071,19 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) return; } w1inv = -1.0f / w1; - prev_xy = pvmtx[0][1] * prev_y; prev_y_terms = pvmtx[1][1] * prev_y; - cur_y_term = pvmtx[1][1] * pp->pos.y; - prev_x_sum = pvmtx[0][0] * prev_x + prev_xy; - prev_yx = pvmtx[1][0] * prev_x + prev_y_terms; - cur_yx = pvmtx[1][0] * pp->pos.x + cur_y_term; + prev_x_sum = pvmtx[0][0] * prev_x + pvmtx[0][1] * prev_y; pv03 = pvmtx[0][3]; pv13 = pvmtx[1][3]; x = w0inv * (pv03 + (pvmtx[0][2] * pp->pos.z + (pvmtx[0][0] * pp->pos.x + pvmtx[0][1] * pp->pos.y))) - w1inv * (pv03 + (pvmtx[0][2] * prev_z + prev_x_sum)); - y = w0inv * (pv13 + (pvmtx[1][2] * pp->pos.z + cur_yx)) - - w1inv * (pv13 + (pvmtx[1][2] * prev_z + prev_yx)); + y = w0inv * (pv13 + (pvmtx[1][2] * pp->pos.z + + (pvmtx[1][0] * pp->pos.x + + pvmtx[1][1] * pp->pos.y))) - + w1inv * (pv13 + (pvmtx[1][2] * prev_z + + (pvmtx[1][0] * prev_x + prev_y_terms))); } else if (pp->kind & Tornado) { f32 prev_x; f32 prev_y; @@ -1571,9 +1565,7 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) setVtxDesc(3); GXBegin(GX_QUADS, GX_VTXFMT3, 4U); } - GXWGFifo.f32 = -ax + prev_pos.x; - GXWGFifo.f32 = -ay + prev_pos.y; - GXWGFifo.f32 = prev_pos.z; + GXPosition3f32(-ax + prev_pos.x, -ay + prev_pos.y, prev_pos.z); { f32 alpha = (f32) draw_color.a * pp->trail; u8 b = draw_color.b; @@ -1610,9 +1602,7 @@ static inline void psDispSubAppSRT(HSD_Particle* pp, u8* texform) if (pp->kind & DispTexture) { GXWGFifo.u8 = ((pp->kind >> 16) & 0xC) + 2; } - GXWGFifo.f32 = bx + prev_pos.x; - GXWGFifo.f32 = by + prev_pos.y; - GXWGFifo.f32 = prev_pos.z; + GXPosition3f32(bx + prev_pos.x, by + prev_pos.y, prev_pos.z); { u8 a = draw_color.a; f32 alpha = (f32) a * pp->trail; From 9c6ef4d37aecceec98894e503daf5ae4d9767977 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 10:21:07 -0700 Subject: [PATCH 74/79] psdisp: lead the tornado previous position with its depth psDispSubAppSRT already declares the matching triple depth-first; doing the same in psDispSub settles two more of its frame displacements. --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 6249d977a6..604a267e16 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1085,9 +1085,9 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) w1inv * (pv13 + (pvmtx[1][2] * prev_z + (pvmtx[1][0] * prev_x + prev_y_terms))); } else if (pp->kind & Tornado) { + f32 prev_z; f32 prev_x; f32 prev_y; - f32 prev_z; f32 dx; f32 dy; f32 dz; From 83d7ffef881bc9e4421038e06b1a2bcc19d87db7 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 12:36:40 -0700 Subject: [PATCH 75/79] psdisp: lead the projection products with their position operands Six of the perspective-divide products take their position operand first. The pattern is consistent: terms built from the local prev_x/y/z and from pp->pos lead with the position, while the remaining matrix-first terms stay as they were. Same diff row count, but fewer registers wrong per row. --- src/sysdolphin/baselib/psdisp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 604a267e16..6773cecd8b 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1065,24 +1065,24 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) return; } w0inv = -1.0f / w0; - w1 = vmtx[2][3] + (vmtx[2][2] * prev_z + - (vmtx[2][1] * prev_y + vmtx[2][0] * prev_x)); + w1 = vmtx[2][3] + (prev_z * vmtx[2][2] + + (vmtx[2][1] * prev_y + prev_x * vmtx[2][0])); if (0.0f == w1) { return; } w1inv = -1.0f / w1; prev_y_terms = pvmtx[1][1] * prev_y; - prev_x_sum = pvmtx[0][0] * prev_x + pvmtx[0][1] * prev_y; + prev_x_sum = prev_x * pvmtx[0][0] + pvmtx[0][1] * prev_y; pv03 = pvmtx[0][3]; pv13 = pvmtx[1][3]; x = w0inv * (pv03 + (pvmtx[0][2] * pp->pos.z + (pvmtx[0][0] * pp->pos.x + pvmtx[0][1] * pp->pos.y))) - w1inv * (pv03 + (pvmtx[0][2] * prev_z + prev_x_sum)); - y = w0inv * (pv13 + (pvmtx[1][2] * pp->pos.z + + y = w0inv * (pv13 + (pp->pos.z * pvmtx[1][2] + (pvmtx[1][0] * pp->pos.x + - pvmtx[1][1] * pp->pos.y))) - - w1inv * (pv13 + (pvmtx[1][2] * prev_z + + pp->pos.y * pvmtx[1][1]))) - + w1inv * (pv13 + (prev_z * pvmtx[1][2] + (pvmtx[1][0] * prev_x + prev_y_terms))); } else if (pp->kind & Tornado) { f32 prev_z; From c6cec47d6a9e30a79d7085bb18fb81f5391d9f5e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 13:22:56 -0700 Subject: [PATCH 76/79] psdisp: lead the previous X product in the projected Y term The last of the perspective-divide products to take its position operand first; only reachable once clang-format had reflowed the surrounding expression. --- src/sysdolphin/baselib/psdisp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 6773cecd8b..41144d760f 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1083,7 +1083,7 @@ static inline void psDispSub(HSD_Particle* pp, u8* texform) (pvmtx[1][0] * pp->pos.x + pp->pos.y * pvmtx[1][1]))) - w1inv * (pv13 + (prev_z * pvmtx[1][2] + - (pvmtx[1][0] * prev_x + prev_y_terms))); + (prev_x * pvmtx[1][0] + prev_y_terms))); } else if (pp->kind & Tornado) { f32 prev_z; f32 prev_x; From 93851ac4b9295cabda8d7690926fbaf159ed83fd Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 14:41:32 -0700 Subject: [PATCH 77/79] psdisp: birth the APPSRT point Y component before its first use --- src/sysdolphin/baselib/psdisp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 41144d760f..df838a6d0f 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -1247,6 +1247,7 @@ static inline void psDispSubAPPSRTPoint(HSD_Particle* pp) HSD_psAppSRT* appsrt = pp->appsrt; f32 y_component; + (void) y_component; cur_x = appsrt->ssy * (y_component = pp->pos.y); cur_y = appsrt->x78 * y_component; cur_z = appsrt->x88 * y_component; From eda7ef50583b0df212bef0d803d9a1a88a8afd18 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 16:54:12 -0700 Subject: [PATCH 78/79] psdisp: birth the point trail colour walker before the line width setup --- src/sysdolphin/baselib/psdisp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index df838a6d0f..4f729dbca8 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -619,6 +619,7 @@ static inline HSD_Particle* psDispSubPointTrail(HSD_Particle* pp) s32 i; s32 w; + (void) c; psSetCurrentMtx(GX_PNMTX0); w = (s32) ((pp->size > 42.5) ? 255.0f : 6.0f * pp->size); if (prevLineWidth != (s32) (u8) w) { From 6c497092309b2d136dd4b790f1abda806fc2c8ec Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 31 Aug 2026 18:59:54 -0700 Subject: [PATCH 79/79] psdisp: keep the point trail vertex walker live to the function exit --- src/sysdolphin/baselib/psdisp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sysdolphin/baselib/psdisp.c b/src/sysdolphin/baselib/psdisp.c index 4f729dbca8..832225ba8e 100644 --- a/src/sysdolphin/baselib/psdisp.c +++ b/src/sysdolphin/baselib/psdisp.c @@ -741,6 +741,7 @@ static inline HSD_Particle* psDispSubPointTrail(HSD_Particle* pp) c += 2; } } + (void) p; return last; }