diff --git a/code/duffle/gen/macs.h b/code/duffle/gen/macs.h index bae3ef8..2069ac4 100644 --- a/code/duffle/gen/macs.h +++ b/code/duffle/gen/macs.h @@ -203,8 +203,7 @@ WORD_COUNT(mac_gte_gpf_scale, 13) * MAC1 = RT11*V0.x + RT12*V0.y + RT13*V0.z + 0 * MAC2 = RT21*V0.x + RT22*V0.y + RT23*V0.z + 0 * MAC3 = RT31*V0.x + RT32*V0.y + RT33*V0.z + 0 - * Side effect: RTPS also writes SXY0/1/2 and SZ0..SZ3 (perspective projection). Ignored. */ \ -, gte_cmdw_rtps_no_tr /* Read MAC1/2/3 → out. */ \ + * Side effect: RTPS also writes SXY0/1/2 and SZ0..SZ3 (perspective projection). Ignored. */ /* gte_cmdw_rtps_no_tr, /* Read MAC1/2/3 → out. */ */ \ , gte_mv_from_data_r(r_t0, C2_MAC1) \ , gte_mv_from_data_r(r_t1, C2_MAC2) \ , gte_mv_from_data_r(r_t2, C2_MAC3) \ @@ -212,7 +211,7 @@ WORD_COUNT(mac_gte_gpf_scale, 13) , store_word(r_t0, r_out, 0) \ , store_word(r_t1, r_out, 4) \ , store_word(r_t2, r_out, 8) -WORD_COUNT(mac_apply_matrix_lv, 31) +WORD_COUNT(mac_apply_matrix_lv, 30) #define mac_trans_matrix(r_mtx, r_off, r_t0, r_t1) \ load_word(r_t0, r_mtx, O_(MT3_S2S4,t[0])) \ diff --git a/code/duffle/gte.atom.c b/code/duffle/gte.atom.c index 47ddeac..9e3224a 100644 --- a/code/duffle/gte.atom.c +++ b/code/duffle/gte.atom.c @@ -131,7 +131,7 @@ FI_ Slice_MipsCode ac_apply_matrix_lv(AtomBuilder_R ab * MAC2 = RT21*V0.x + RT22*V0.y + RT23*V0.z + 0 * MAC3 = RT31*V0.x + RT32*V0.y + RT33*V0.z + 0 * Side effect: RTPS also writes SXY0/1/2 and SZ0..SZ3 (perspective projection). Ignored. */ - gte_cmdw_rtps_no_tr, + // gte_cmdw_rtps_no_tr, /* Read MAC1/2/3 → out. */ gte_mv_from_data_r(r_t0, C2_MAC1), diff --git a/code/hello_camera/hello_camera.atom.c b/code/hello_camera/hello_camera.atom.c index 38e2deb..db50fde 100644 --- a/code/hello_camera/hello_camera.atom.c +++ b/code/hello_camera/hello_camera.atom.c @@ -395,16 +395,42 @@ I_ MipsAtom* resolve_look_at__cross_uz_ux_to_up_proc(AtomArena_R aa, U4 r_scratc load_word(R_V0, r_h, O_(V3_S4,z)), nop, - /* mtc2 a → IR1/2/3, b → D1/2/3 (VXY0/VZ0/VXY1). */ - gte_mv_to_data_r(r_a, C2_IR1), - gte_mv_to_data_r(r_b, C2_IR2), - gte_mv_to_data_r(r_c, C2_IR3), - gte_mv_to_data_r(r_d, C2_VXY0), - gte_mv_to_data_r(R_AT, C2_VZ0), - gte_mv_to_data_r(R_V0, C2_VXY1), - nop2, + /* FIX 2026-08-11: OP reads D1/D2/D3 from RT11/RT22/RT33 control registers + * ($0/$2/$4), NOT from V0/V1/V2 input data registers. The previous body + * wrote ux to C2_VXY0/VZ0/VXY1 — D1/D2/D3 were whatever stale RT values + * the previous atom left, so the GTE computed garbage MAC values. Fix: + * mirror atom 1's pattern (cfc2 RT11/RT22 save + ctc2 RT13/RT22/RT11 + * load uz diagonal + mtc2 IR1/2/3 load ux + ctc2 RT restore). + * See SESSION_2026-08-11b §5.2 for the empirical verification. */ + + /* Save the two RT control-register slots OP will clobber (reusing + * r_g/r_h — they're no longer needed as scratch pointers). */ + gte_mv_from_ctrl_r(r_g, gte_cr_RT11), /* r_g = C2 $0 (RT11|RT12) */ + gte_mv_from_ctrl_r(r_h, gte_cr_RT22), /* r_h = C2 $4 (RT22|RT33) */ + + /* Load uz into the RT diagonal — same packing as atom 1. + * OP reads D1 = RT11 from $0.low, D2 = RT22 from $2.high, D3 = RT33 from $4.high. + * RT22 is shared between $2.high and $4.low — the ctc2 sequence to $2 then $4 + * sets RT22 to uz.y.high (via $2), then to uz.z.low (via $4). OP reads + * RT22 from $2.high which the second ctc2 doesn't touch, so D2 stays uz.y.high. + * (This is libpsyx OuterProduct12 convention EXACTLY.) */ + gte_mv_to_ctrl_r(r_b, gte_cr_RT13), /* $2 = uz.y. RT13=uz.y.low, RT22=uz.y.high. */ + gte_mv_to_ctrl_r(r_c, gte_cr_RT22), /* $4 = uz.z. RT22=uz.z.low, RT33=uz.z.high. */ + gte_mv_to_ctrl_r(r_a, gte_cr_RT11), /* $0 = uz.x. RT11=uz.x. */ + nop2, /* CTC2 retirement (CPU→COP2 2-slot delay) */ + + /* Load ux into the IR registers (the second operand for OP). */ + gte_mv_to_data_r(r_d, C2_IR1), /* IR1 = ux.x */ + gte_mv_to_data_r(R_AT, C2_IR2), /* IR2 = ux.y */ + gte_mv_to_data_r(R_V0, C2_IR3), /* IR3 = ux.z */ + nop2, /* MTC2 retirement (CPU→COP2 2-slot delay) */ gte_cmdw_outer_product, + + /* Restore the RT slots we clobbered. */ + gte_mv_to_ctrl_r(r_g, gte_cr_RT11), /* restore C2 $0 (RT11|RT12) */ + gte_mv_to_ctrl_r(r_h, gte_cr_RT22), /* restore C2 $4 (RT22|RT33) */ + gte_mv_from_data_r(r_a, C2_MAC1), gte_mv_from_data_r(r_b, C2_MAC2), gte_mv_from_data_r(r_c, C2_MAC3), diff --git a/code/hello_camera/hello_camera.c b/code/hello_camera/hello_camera.c index b9cc7d4..58552c1 100644 --- a/code/hello_camera/hello_camera.c +++ b/code/hello_camera/hello_camera.c @@ -280,8 +280,8 @@ I_ void resolve_look_at( tb_emit(tb, smem.resolve_look_at_atom_addrs[1]); { } tb_emit(tb, smem.resolve_look_at_atom_addrs[2]); { } tb_emit(tb, smem.resolve_look_at_atom_addrs[3]); { } - // tb_emit(tb, smem.resolve_look_at_atom_addrs[4]); { } - // tb_emit(tb, smem.resolve_look_at_atom_addrs[5]); { } + tb_emit(tb, smem.resolve_look_at_atom_addrs[4]); { } + tb_emit(tb, smem.resolve_look_at_atom_addrs[5]); { } // /* Atom 6: populate_and_translate — only output pointer is the matrix destination. */ // tb_emit(tb, smem.resolve_look_at_atom_addrs[6]); { @@ -361,7 +361,7 @@ void update(PrimitiveArena* pa, U4* ordering_buf) right = scratch->right; ux = scratch->ux; - cross_v3s4(& uz, & ux, & up); normalize_v3s4(& up, & uy); + // cross_v3s4(& uz, & ux, & up); normalize_v3s4(& up, & uy); smem.cam.look_at.m[0][0] = ux.x; smem.cam.look_at.m[0][1] = ux.y; smem.cam.look_at.m[0][2] = ux.z; smem.cam.look_at.m[1][0] = uy.x; smem.cam.look_at.m[1][1] = uy.y; smem.cam.look_at.m[1][2] = uy.z; @@ -369,7 +369,8 @@ void update(PrimitiveArena* pa, U4* ordering_buf) pos = smem.cam.pos; mul_v3s4(& pos, v3s4(-1,-1,-1)); // RGA(Lengyel): -eye in world coordinates (spatial bulk only; implicit weight is dropped). - mul_m3s2_v3s4(& smem.cam.look_at, & pos, & off); trans_m3s2( & smem.cam.look_at, & off); + mul_m3s2_v3s4(& smem.cam.look_at, & pos, & off); + trans_m3s2( & smem.cam.look_at, & off); } // Draw cube