First real attribution (in-process 1 kHz IP sampler, 75k samples at
oc900, play build). Top sinks and their fixes:
- tex_pack_on_textured_prim (7.4%): per-prim mutex + CLUT hash + linear
scan over up to 8192 uploads — entirely matched-set/dump census, a
debug/authoring surface with no reader in play builds (substitution
lives in tex_pack_lookup_replacement). Gated out of play builds
unless dumping. Confirmed gone in the follow-up profile.
- text_xlate_on_dispatch (4.2% -> 0.8%): the a0..a3 record scan serves
capture (off in release) and STRING substitution; a fixes-only load
(vram patches / glyph labels) has an empty string table, so the scan
could never act. Early-out on empty table; the throttled patch blocks
still run.
- psx_pgxp_alu/muldiv (3.6%): ALU/MULDIV shadows feed only tier-2
cpu_mode and the correction consumers' chains; the exact-sign NCLIP
arm is LOAD/STORE/COP2 only. Hook bodies now gate on
(cpu_mode || full_hooks) — full_hooks maintained by
gpu_pgxp_rederive_enable — and the remaining per-instruction CALL
cost is removed by an inline g_pgxp_alu_armed check in the
PGXP_ALU/PGXP_MULDIV macros themselves. Correctness: a stale shadow
from the gated period is dropped by pv_validate at every consumer
(the engine's single safety invariant, verified at the MTC2 path).
Measured effect of the round at oc900 (before the macro gate):
sustained 0.73-0.80x -> 0.87-0.91x; streaming-window dip 0.35 -> 0.50;
post-window touching 1.00x. Fresh profile after: xlate and tex_pack
prim hook out of the top-15.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Root-causes the transient 1-frame sign-corner flicker at wide aspects
(sign faces blink when the exact-sign NCLIP path silently loses its
shadows for one frame):
- gte_nclip was the only shadow consumer skipping the engine's single
safety invariant (packed-word validation). A stale FIFO shadow (e.g.
after an SXYP MTC2 shifts the real FIFO but not shadows 12/13) could
invent a silently WRONG winding sign. New pgxp_get_gte_sxy_checked
validates the shadow's word against the live SXY register; staleness
now demotes to the honest integer fallback.
- Overlay shadow-diff REPLAY passes ran the pgxp memory hooks live
(record pass's stale FIFO shadows re-validated against different
vertices -> flags dropped, live slots overwritten -> arena-wide
provenance kill = one-frame integer-sign blink, plus false
SHDIFF_GTE_DATA divergences permanently demoting shards to interp).
gte_replay_side_effects_begin/end now bracket pgxp_suppress so replay
is shadow-silent, like the speculative validation passes. The nclip
precise path is additionally gated off inside the sandbox and
speculative passes.
- pgxp_set_enabled is now idempotent: a redundant re-derive (config
apply, correction toggle, debug A/B) no longer costs a generation
bump, which killed every live shadow and blinked one frame of
exact-sign coverage.
- Observability: free-running NCLIP precise/fallback/corrected counters
and a PGXPStats.invalidations counter (bumped at the generation-bump
site), both exposed in the geom_correction debug reply.
All changes are inert at 4:3 (ws_precise_nclip gate) and touch
host-only state exclusively.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two coverage holes measured live on WipEout 3 SE:
- The shadow was allocated lazily ONCE; when the first enable ran before the
8 MB mod expanded RAM, a 2 MB-wrapped shadow aliased every extended-RAM
packet for the whole session (the player ship's packets resolved fully
native - flickering ship textures, dark track dashes). Re-size on any
enable whose live RAM size disagrees.
- Every half-rebuild recovery path (SH pair, LH/LHU, SLL/SRL/SRA by 16,
OR repack, ADD/SUB screen translate) preserved positions but dropped the
vertex depth, disarming perspective UVs on every packet the game builds
from halves ("swimming" wall panels). Depth now rides along whenever the
contributing sources agree; the truncation-agreement gate still owns
mixed-source rebuilds.
Adds pgxp_debug_shadow_class: which rung of the load ladder fails for a
word, for the texcensus diagnostic.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Emitter side of the value-propagation engine (ENHANCEMENTS.md G1.10):
- recompiler/src/pgxp_hook_emitter.{h,cpp} (new, in the codegen-hash
surface): ONE hook grammar shared by BOTH emitters, wrapping loads/
stores/COP2 transfers and precision-carrying ALU ops (moves, add/sub,
or-merge, shifts, LUI, HI/LO, muldiv) with PGXP_*() macros. Clobberable
sources are captured into locals before the statement. Precision-
destroying ops (AND/XOR/SLT/...) are deliberately unhooked - validate-
on-read drops their stale shadows for free.
- code_generator.cpp: central append in translate_instruction; LWC2/SWC2
emissions capture the RAW loaded/stored word (a masked GTE register
write must validate against the word as loaded).
- strict_translator.cpp: translate() wraps translate_impl()'s c_code with
the shared grammar; deferred load-delay variants hook the psx_ldd temp.
- BIOS regenerated, fingerprints verified CHANGED (SCPH1001 + OpenBIOS;
42.6k/12.2k macro sites). Base runtime + 33/33 tests green against it.
- Gating: macros expand only under -DPSX_PGXP=1. runtime.cmake grows a
PGXP option on psxrecomp_add_runtime_target (defines PSX_PGXP=1 +
PSX_OVERLAY_FLAVOR=2, _pgxp exe suffix) and PSX_PGXP_VARIANT to build
the sibling target from the same generated tree (framework runtime +
game wrapper). No ABI version bump needed: the appended PGXPHooks
table is NULL-tolerant and the flavor half of the ABI tag rejects any
host/DLL flavor mix.
- Shard caches: flavor folded into the cache tag (cg<V>_<hash>_gc<hash>
_f<flavor>) in overlay_loader.c + compile_overlays.py so base and pgxp
caches never share a directory; compile_overlays.py passes
-DPSX_PGXP=1 when flavor bit 2 is set; overlay preamble forwards
psx_pgxp_* through the table. PSX_OVERLAY_CODEGEN_VER 9 -> 10.
- Env overrides PSX_GEOMETRY_CORRECTION / PSX_PERSPECTIVE_TEXTURING /
PSX_PGXP_CPU_MODE (validation path: free-running headless boots arm
from the first vertex); census grew produced/swc2_stores and the pgxp
verb reports suppress/active gate state.
Validated: PSXRecomp_pgxp.exe boots the recompiled BIOS with hooks armed
(engine active, per-vertex lookups flowing; the BIOS shell has no RTPS
content so produced=0 there is correct - coverage measurement moves to a
3D title in phase 3).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>