Merge feat/render-pass-kit (ROTATION, counters, offline build fix)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthew Stanley
2026-10-02 10:33:50 -07:00
6 changed files with 59 additions and 18 deletions
+3 -3
View File
@@ -62,9 +62,6 @@ int psx_mod_finish_function(struct CPUState* cpu);
/* Active function-entry hook count (0 = none). Hot callers test it before the
* call, so a run without an active hook pays one load per interpreted entry. */
extern uint32_t g_psx_mod_function_entry_hooks;
/* Entry callbacks can make nested guest calls while retaining host registers.
* Save/load and rewind must wait until that host context has returned. */
int psx_mod_function_entry_active(void);
/* Always-on named event counters for trusted plugins (observability, not
* logging): e.g. how often each guard in a hook rejected. `name` should be a
@@ -72,6 +69,9 @@ int psx_mod_function_entry_active(void);
* kept, further names are counted in an overflow bucket. Emulation-thread only.
* TCP: {"cmd":"mod_counters"} lists every counter with its last frame. */
void psx_mod_counter_add(const char* name, uint32_t delta);
/* Entry callbacks can make nested guest calls while retaining host registers.
* Save/load and rewind must wait until that host context has returned. */
int psx_mod_function_entry_active(void);
/* Narrow guest services available to trusted plugin callbacks. */
int psx_mod_game_started(void);
+6 -2
View File
@@ -45,7 +45,11 @@ enum {
PSX_MOTION_SVECTOR = 4, /* s16 x, y, z */
/* s16 x, y, z angles, 4096 per turn: each blends along the shorter arc. */
PSX_MOTION_ANGLES = 5,
PSX_MOTION_SCALAR = 6 /* s32 */
PSX_MOTION_SCALAR = 6, /* s32 */
/* A bare 3x3 s16 rotation in 4.12 (18 bytes), blended like a MATRIX's;
* pair it with a VECTOR / SVECTOR kind wherever its translation lives
* (skeleton pose records, matrices stored apart from their position). */
PSX_MOTION_ROTATION = 7
};
typedef struct PSXMotionLimits {
@@ -92,7 +96,7 @@ uint32_t psx_motion_prepare(PSXMotionSet* set, const PSXMotionLimits* limits,
/* Write every prepared value at t (0 = previous frame, 1 = this frame). */
void psx_motion_apply(const PSXMotionSet* set, double t);
/* Blend one prepared value at t into `out` without writing guest RAM:
* a PSXMotionMatrix for the MATRIX kinds, int32_t[3] for VECTOR / SVECTOR /
* a PSXMotionMatrix for the MATRIX kinds and ROTATION (translation 0), int32_t[3] for VECTOR / SVECTOR /
* ANGLES, int32_t for SCALAR. Returns 0 when that value does not blend this
* frame (out then holds its current value) or was not tracked (out
* untouched, returns -1). */
+8 -8
View File
@@ -7802,14 +7802,6 @@ static void handle_input_route_status(int id, const char *json)
* {"cmd":"ws_hud_mode","tag_rects":0|1}
* tag_rects=1 lets TAGGED rect-family prims re-anchor too (Tomba's AP
* counter renders through the tagged sprite funnel). */
static void handle_ws_hud_mode(int id, const char *json)
{
int v = json_get_int(json, "tag_rects", -1);
if (v < 0) { send_err(id, "missing tag_rects (0|1)"); return; }
gpu_ws_set_nw_hud_tag_rects(v);
send_fmt("{\"id\":%d,\"ok\":true,\"tag_rects\":%d}", id, v ? 1 : 0);
}
/* Named plugin counters (psx_mod_counter_add), always on:
* {"cmd":"mod_counters"}
* -> counters: [{name, count, last_frame}], overflow. */
@@ -7849,6 +7841,14 @@ static void handle_mod_counters(int id, const char *json)
free(buf);
}
static void handle_ws_hud_mode(int id, const char *json)
{
int v = json_get_int(json, "tag_rects", -1);
if (v < 0) { send_err(id, "missing tag_rects (0|1)"); return; }
gpu_ws_set_nw_hud_tag_rects(v);
send_fmt("{\"id\":%d,\"ok\":true,\"tag_rects\":%d}", id, v ? 1 : 0);
}
/* Explicit HUD-anchor / background tag pipeline counters (always on):
* {"cmd":"ws_tag_stats"}
* -> hud / background: tag_calls, tag_rejected, hit, stale, guard_mismatch,
+6
View File
@@ -62,7 +62,9 @@ extern "C" void psx_event_step_conservative_env_init(void);
#include "psx_netplay_rb.h"
#include "psx_selfcheck.h"
#include "psx_lobby_client.h"
#if defined(PSX_HAS_RECOMP_NET)
#include "recomp_net/host_relay.h" /* RNetHostRelayStatus for the launcher relay line */
#endif
#include "netplay_bios_settle.h"
#include "netplay_exit_reason.h"
#include "host_time.h"
@@ -11024,6 +11026,7 @@ namespace {
if (!out || !out_cap) return 0;
out[0] = '\0';
if (g_lnch_hosting_lan || g_lnch_joined_lan || !psx_lobby_in_lobby()) return 0;
#if defined(PSX_HAS_RECOMP_NET)
RNetHostRelayStatus st;
if (!psx_lobby_host_relay_status(&st)) return 0;
if (st.role == 1) {
@@ -11066,6 +11069,9 @@ namespace {
return 1;
}
return 0;
#else
return 0; /* offline build: recomp-net (and its host relay) is not linked */
#endif
}
int ae_np_force_turn_set(void*, int force) {
if (g_lnch_hosting_lan || g_lnch_joined_lan)
+14 -5
View File
@@ -108,6 +108,9 @@ static void read_entry(MotionEntry* e) {
for (int i = 0; i < 3; i++) e->v[9 + i] = m.t[i];
break;
}
case PSX_MOTION_ROTATION:
for (int i = 0; i < 9; i++) e->v[i] = (int16_t)psx_mod_read_half(e->addr + 2u * (uint32_t)i);
break;
case PSX_MOTION_VECTOR:
for (int i = 0; i < 3; i++) e->v[i] = (int32_t)psx_mod_read_word(e->addr + 4u * (uint32_t)i);
break;
@@ -123,7 +126,7 @@ static void read_entry(MotionEntry* e) {
int psx_motion_track(PSXMotionSet* s, uint32_t kind, uint32_t addr, uint32_t identity) {
MotionEntry* e;
if (!s || kind < PSX_MOTION_MATRIX || kind > PSX_MOTION_SCALAR) return 0;
if (!s || kind < PSX_MOTION_MATRIX || kind > PSX_MOTION_ROTATION) return 0;
if (!s->began[s->cur] || s->n[s->cur] >= s->cap) return 0;
e = &s->cap_buf[s->cur][s->n[s->cur]];
memset(e, 0, sizeof *e);
@@ -270,10 +273,11 @@ static int within_limits(const MotionEntry* a, const MotionEntry* b,
const PSXMotionLimits* lim, uint32_t ticks) {
const double move = lim->move_per_tick > 0 ? lim->move_per_tick * ticks : HUGE_VAL;
switch (a->kind) {
case PSX_MOTION_MATRIX: {
case PSX_MOTION_MATRIX:
case PSX_MOTION_ROTATION: {
double ang;
int16_t ra[9], rb[9];
if (dist3(&a->v[9], &b->v[9]) > move) return 0;
if (a->kind == PSX_MOTION_MATRIX && dist3(&a->v[9], &b->v[9]) > move) return 0;
for (int i = 0; i < 9; i++) { ra[i] = (int16_t)a->v[i]; rb[i] = (int16_t)b->v[i]; }
ang = rotation_angle(ra, rb);
if (ang >= 0) return ang <= turn_limit(lim->turn_radians);
@@ -344,7 +348,8 @@ uint32_t psx_motion_prepare(PSXMotionSet* s, const PSXMotionLimits* lim,
/* Blend one pair into `v` (entry layout). */
static void blend_entry(const MotionEntry* a, const MotionEntry* b, double t, int32_t* v) {
switch (a->kind) {
case PSX_MOTION_MATRIX: {
case PSX_MOTION_MATRIX:
case PSX_MOTION_ROTATION: {
int16_t ra[9], rb[9], ro[9];
for (int i = 0; i < 9; i++) { ra[i] = (int16_t)a->v[i]; rb[i] = (int16_t)b->v[i]; }
blend_rotation(ra, rb, t, MOTION_PI * 2, ro); /* limits were applied at prepare */
@@ -388,6 +393,9 @@ static void write_entry(uint8_t kind, uint32_t addr, const int32_t* v) {
case PSX_MOTION_SCALAR:
psx_mod_write_word(addr, (uint32_t)v[0]);
break;
case PSX_MOTION_ROTATION:
for (int i = 0; i < 9; i++) psx_mod_write_half(addr + 2u * (uint32_t)i, (uint16_t)v[i]);
break;
}
}
@@ -405,7 +413,8 @@ void psx_motion_apply(const PSXMotionSet* s, double t) {
}
static void export_value(uint8_t kind, const int32_t* v, void* out) {
if (kind == PSX_MOTION_MATRIX || kind == PSX_MOTION_MATRIX_TRANSLATION) {
if (kind == PSX_MOTION_MATRIX || kind == PSX_MOTION_MATRIX_TRANSLATION ||
kind == PSX_MOTION_ROTATION) {
PSXMotionMatrix* m = (PSXMotionMatrix*)out;
for (int i = 0; i < 9; i++) m->r[i] = (int16_t)v[i];
for (int i = 0; i < 3; i++) m->t[i] = v[9 + i];
+22
View File
@@ -164,6 +164,27 @@ static void test_angles_vectors_scalars(void) {
psx_motion_set_destroy(s);
}
static void test_rotation_only(void) {
PSXMotionSet* s = psx_motion_set_create(4);
PSXMotionLimits lim = {100.0, 0};
PSXMotionStats st;
/* A bare rotation (as in a pose record) with other data right after it. */
put_matrix(0xA00, 0.0, 0, 0, 0);
psx_mod_write_word(0xA00 + 0x12, 0xDEADBEEFu);
psx_motion_begin(s, 1);
psx_motion_track(s, PSX_MOTION_ROTATION, 0xA00, 3);
psx_motion_prepare(s, &lim, &st);
put_matrix(0xA00, 1.0, 0, 0, 0);
psx_mod_write_word(0xA00 + 0x12, 0xDEADBEEFu);
psx_motion_begin(s, 2);
psx_motion_track(s, PSX_MOTION_ROTATION, 0xA00, 3);
CHECK(psx_motion_prepare(s, &lim, &st) == 1, "a bare rotation blends");
psx_motion_apply(s, 0.5);
CHECK(fabs(yaw_of(0xA00) - 0.5) < 0.002, "rotation-only slerps");
CHECK(psx_mod_read_word(0xA00 + 0x12) == 0xDEADBEEFu, "nothing past the 18 bytes is written");
psx_motion_set_destroy(s);
}
static int s_fn_calls;
static double s_fn_last;
static int frame_fn(struct CPUState* cpu, void* user, uint32_t alpha) {
@@ -209,6 +230,7 @@ int main(void) {
test_matrix_slerp();
test_identity_and_limits();
test_angles_vectors_scalars();
test_rotation_only();
test_frame_driver();
printf(failures ? "FAILED (%d)\n" : "ALL PASS\n", failures);
return failures ? 1 : 0;