mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
PGXP: always-on ring of refused perspective-depth lookups (TCP pgxp_miss_ring)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -127,6 +127,26 @@ void pgxp_store_gte_reg(uint32_t addr, uint8_t reg);
|
||||
int pgxp_load_precise_word(uint32_t addr, uint32_t packed,
|
||||
int32_t *x16, int32_t *y16, uint16_t *z);
|
||||
|
||||
/* Refused precise-word lookups, newest at (seq - 1) % cap (TCP pgxp_miss_ring). */
|
||||
enum {
|
||||
PGXP_MISS_UNTRACKED = 1,
|
||||
PGXP_MISS_MISMATCH = 2,
|
||||
PGXP_MISS_PARTIAL = 3,
|
||||
PGXP_MISS_NO_Z = 4
|
||||
};
|
||||
#define PGXP_MISS_RING_CAP 8192u
|
||||
typedef struct PGXPWordMiss {
|
||||
uint64_t seq;
|
||||
uint32_t addr; /* packet word address (canonical RAM offset) */
|
||||
uint32_t packet; /* the word the GPU consumed */
|
||||
uint32_t shadow_value; /* the word the shadow describes */
|
||||
uint8_t shadow_flags; /* 1 = X, 2 = Y, 4 = Z */
|
||||
uint8_t live; /* shadow belongs to the current generation */
|
||||
uint8_t reason; /* PGXP_MISS_* */
|
||||
} PGXPWordMiss;
|
||||
/* Returns the total number of misses ever recorded. */
|
||||
uint64_t pgxp_word_miss_ring(const PGXPWordMiss **ring, uint32_t *cap);
|
||||
|
||||
/* Debug read of one shadow slot (TCP pgxp_shadow). `space`: 0 = guest
|
||||
* address (RAM / scratchpad), 1 = GPR index (32 = HI, 33 = LO), 2 = GTE data
|
||||
* register. Returns 0 when the slot does not exist; *live says whether it
|
||||
|
||||
@@ -5353,6 +5353,40 @@ static void handle_pgxp_shadow(int id, const char *json)
|
||||
send_fmt("%s", buf);
|
||||
}
|
||||
|
||||
/* pgxp_miss_ring — the newest refused perspective-depth lookups (always-on
|
||||
* ring, PGXP_MISS_RING_CAP deep): {"cmd":"pgxp_miss_ring","count":N}. Each
|
||||
* entry: packet word address (canonical RAM offset), the word the GPU
|
||||
* consumed, the word/flags the shadow held, and the reason (untracked,
|
||||
* mismatch, partial, no_z). Join addr with wtrace_dump to name the writer. */
|
||||
static void handle_pgxp_miss_ring(int id, const char *json)
|
||||
{
|
||||
static const char *const reasons[] = { "?", "untracked", "mismatch",
|
||||
"partial", "no_z" };
|
||||
const PGXPWordMiss *ring; uint32_t cap;
|
||||
uint64_t total = pgxp_word_miss_ring(&ring, &cap);
|
||||
int count = json_get_int(json, "count", 256);
|
||||
uint64_t avail = total < cap ? total : cap;
|
||||
if (count < 0) count = 0;
|
||||
if ((uint64_t)count > avail) count = (int)avail;
|
||||
size_t sz = (size_t)count * 160u + 256u;
|
||||
char *buf = (char *)malloc(sz);
|
||||
if (!buf) { send_err(id, "oom"); return; }
|
||||
size_t n = (size_t)snprintf(buf, sz, "{\"id\":%d,\"ok\":true,\"total\":%llu,\"entries\":[",
|
||||
id, (unsigned long long)total);
|
||||
for (int i = 0; i < count; i++) {
|
||||
const PGXPWordMiss *m = &ring[(total - (uint64_t)count + (uint64_t)i) % cap];
|
||||
n += (size_t)snprintf(buf + n, sz - n,
|
||||
"%s{\"seq\":%llu,\"addr\":\"0x%08X\",\"packet\":\"0x%08X\","
|
||||
"\"shadow\":\"0x%08X\",\"flags\":%u,\"live\":%u,\"reason\":\"%s\"}",
|
||||
i ? "," : "", (unsigned long long)m->seq, m->addr, m->packet,
|
||||
m->shadow_value, m->shadow_flags, m->live,
|
||||
reasons[m->reason < 5 ? m->reason : 0]);
|
||||
}
|
||||
snprintf(buf + n, sz - n, "]}");
|
||||
send_fmt("%s", buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* pgxp — live-tune the value-propagation engine for one-toggle isolation runs
|
||||
* without a rebuild: {"cmd":"pgxp","cpu_mode":0|1,"tolerance":F}. Fields are
|
||||
* optional; the reply echoes the resulting state (same shape as
|
||||
@@ -14402,6 +14436,7 @@ static const CmdEntry s_commands[] = {
|
||||
{ "geom_correction", handle_geom_correction },
|
||||
{ "pgxp", handle_pgxp },
|
||||
{ "pgxp_shadow", handle_pgxp_shadow },
|
||||
{ "pgxp_miss_ring", handle_pgxp_miss_ring },
|
||||
{ "ws_aspect_cone_site", handle_ws_aspect_cone_site },
|
||||
{ "ws_margin", handle_ws_margin },
|
||||
{ "ws_hud_mode", handle_ws_hud_mode },
|
||||
|
||||
+42
-3
@@ -823,21 +823,60 @@ extern "C" void pgxp_test_set_generation(uint32_t gen) { s_gen = gen; }
|
||||
/* Address-keyed depth lookup for the shipped perspective-texturing path
|
||||
* (gpu.c prepare_texture_triangle). Same contract as the retired hashed
|
||||
* table: hit only when the tracked word matches the packet word exactly. */
|
||||
/* Always-on ring of refused precise-word lookups: which packet word, what
|
||||
* the shadow at its address held, and why it was refused. Join it with
|
||||
* wtrace_dump (writer PC per address) to find the guest code path that drops
|
||||
* provenance. */
|
||||
static PGXPWordMiss s_miss_ring[PGXP_MISS_RING_CAP];
|
||||
static uint64_t s_miss_seq = 0;
|
||||
|
||||
static void note_word_miss(uint32_t addr, uint32_t packed, const PGXPValue *pv,
|
||||
uint8_t reason) {
|
||||
PGXPWordMiss *m = &s_miss_ring[s_miss_seq % PGXP_MISS_RING_CAP];
|
||||
m->seq = s_miss_seq++;
|
||||
m->addr = addr;
|
||||
m->packet = packed;
|
||||
m->shadow_value = pv ? pv->value : 0;
|
||||
m->shadow_flags = pv ? (uint8_t)pv->flags : 0;
|
||||
m->live = (pv && pv->gen == s_gen) ? 1 : 0;
|
||||
m->reason = reason;
|
||||
}
|
||||
|
||||
extern "C" uint64_t pgxp_word_miss_ring(const PGXPWordMiss **ring,
|
||||
uint32_t *cap) {
|
||||
*ring = s_miss_ring;
|
||||
*cap = PGXP_MISS_RING_CAP;
|
||||
return s_miss_seq;
|
||||
}
|
||||
|
||||
extern "C" int pgxp_load_precise_word(uint32_t addr, uint32_t packed,
|
||||
int32_t *x16, int32_t *y16, uint16_t *z) {
|
||||
if (!s_enabled) return 0;
|
||||
s_stats.word_lookups++;
|
||||
PGXPValue *pv = pgxp_ptr(addr);
|
||||
if (!pv || pv->gen != s_gen) { s_stats.word_untracked++; return 0; }
|
||||
if (pv->value != packed) { s_stats.word_mismatch++; return 0; }
|
||||
if (!pv || pv->gen != s_gen) {
|
||||
s_stats.word_untracked++;
|
||||
note_word_miss(addr, packed, pv, PGXP_MISS_UNTRACKED);
|
||||
return 0;
|
||||
}
|
||||
if (pv->value != packed) {
|
||||
s_stats.word_mismatch++;
|
||||
note_word_miss(addr, packed, pv, PGXP_MISS_MISMATCH);
|
||||
return 0;
|
||||
}
|
||||
if ((pv->flags & PGXP_F_VXY) != PGXP_F_VXY) {
|
||||
s_stats.word_partial++;
|
||||
note_word_miss(addr, packed, pv, PGXP_MISS_PARTIAL);
|
||||
return 0;
|
||||
}
|
||||
if (x16) *x16 = pv->x16;
|
||||
if (y16) *y16 = pv->y16;
|
||||
if (z) *z = (pv->flags & PGXP_F_VZ) ? pv->z : 0;
|
||||
if (!(pv->flags & PGXP_F_VZ) || pv->z == 0) { s_stats.word_no_z++; return 0; }
|
||||
if (!(pv->flags & PGXP_F_VZ) || pv->z == 0) {
|
||||
s_stats.word_no_z++;
|
||||
note_word_miss(addr, packed, pv, PGXP_MISS_NO_Z);
|
||||
return 0;
|
||||
}
|
||||
s_stats.word_hit++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user