mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-28 02:56:36 -04:00
8ebfdb4a3d
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>
146 lines
6.6 KiB
C
146 lines
6.6 KiB
C
#ifndef PGXP_H
|
|
#define PGXP_H
|
|
|
|
/* PGXP value-propagation engine — runtime-internal API (pgxp.cpp).
|
|
*
|
|
* The engine shadows every 32-bit word of guest RAM/scratchpad, every GPR,
|
|
* and every GTE data register with the sub-pixel projection it carries
|
|
* (screen X/Y in 16.16 plus the projected SZ depth). The GTE fills shadows at
|
|
* RTPS/RTPT; the psx_pgxp_* hooks (pgxp_hooks.h) move them along with the
|
|
* data; the GPU asks for the precise position of each GP0 vertex word by the
|
|
* packet's RAM address, validated against the actual word — never guessed
|
|
* from the rounded position (the measured G1.4 dead end).
|
|
*
|
|
* Guest-visible state is NEVER touched: shadows are host-only, dropped on
|
|
* savestate/rewind, and suppressed during speculative validation passes.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* --- lifecycle / configuration ------------------------------------------ */
|
|
|
|
/* Master arm. Allocates the RAM shadow lazily; fails closed (stays disabled)
|
|
* if the allocation fails. Idempotent. */
|
|
void pgxp_set_enabled(int enabled);
|
|
int pgxp_enabled(void);
|
|
|
|
/* Tier-2 propagation through CPU arithmetic (default off, like the reference
|
|
* implementations' default). Off is SAFE — value validation already stops
|
|
* stale shadows — it only bounds coverage. Live-tunable (TCP). */
|
|
void pgxp_set_cpu_mode(int enabled);
|
|
int pgxp_cpu_mode(void);
|
|
|
|
/* ALU/MULDIV hook bodies run only when tier-2 cpu_mode OR this flag is set
|
|
* (texture/geometry correction dataflow chains). NCLIP-only arms leave it 0:
|
|
* the SXY chain is LOAD/STORE/COP2. Stale shadows from the gated period are
|
|
* dropped by pv_validate at every consumer (the single safety invariant). */
|
|
void pgxp_set_full_hooks(int on);
|
|
|
|
/* Reject a precise position whose sub-pixel offset from the native integer
|
|
* position exceeds this many pixels. < 0 disables the clamp (default). */
|
|
void pgxp_set_tolerance(float pixels);
|
|
float pgxp_tolerance(void);
|
|
|
|
/* Drop all shadows (savestate load, raw RAM restore, timeline breaks).
|
|
* O(1) via generation bump. Deferred while suppressed. */
|
|
void pgxp_invalidate_all(void);
|
|
|
|
/* Counted suppression bracket for speculative native-validation passes and
|
|
* the GTE replay sandbox: hooks and producers no-op inside it. */
|
|
void pgxp_suppress_begin(void);
|
|
void pgxp_suppress_end(void);
|
|
|
|
/* --- GTE producer (gte.cpp) ---------------------------------------------- */
|
|
|
|
/* Called at the RTPS/RTPT projection with the pre-truncation 16.16 screen
|
|
* coordinates, the projected depth (SZ3), and the packed SXY word the guest
|
|
* sees. Shifts the shadow FIFO exactly like push_sxy (regs 12..15). */
|
|
void pgxp_gte_push_sxy(int32_t x16, int32_t y16, uint16_t sz3, uint32_t packed);
|
|
|
|
/* Current SXY FIFO shadow (index 0..3 selects GTE data regs 12..15).
|
|
* Returns nonzero when the shadow is live and carries X/Y precision. */
|
|
int pgxp_get_gte_sxy(uint32_t index, int32_t *x16, int32_t *y16);
|
|
/* Validate-on-read variant: check!=0 additionally requires the shadow's packed
|
|
* word to equal expect (the live guest SXY word) - the engine's single safety
|
|
* invariant, which sign-consumers must not skip. */
|
|
int pgxp_get_gte_sxy_checked(uint32_t index, uint32_t expect, int check,
|
|
int32_t *x16, int32_t *y16);
|
|
|
|
/* Guest write to a GTE data register outside gte_execute (MTC2/CTC2 handled
|
|
* by psx_pgxp_cop2; this is for direct gte_write_data paths): reg 15 performs
|
|
* the SXYP FIFO push on the shadows, others just invalidate/overwrite. */
|
|
void pgxp_gte_reg_written(int reg, uint32_t value);
|
|
|
|
/* --- GPU consumer (gpu.c) ------------------------------------------------ */
|
|
|
|
enum {
|
|
PGXP_SRC_NATIVE = 0, /* no shadow — caller uses the parsed integers */
|
|
PGXP_SRC_FALLBACK = 1, /* position-cache fallback (never carries depth)*/
|
|
PGXP_SRC_DATAFLOW = 2, /* address-keyed shadow, value-validated */
|
|
};
|
|
|
|
/* Precise position for one GP0 vertex word.
|
|
* addr guest address the word was DMA'd from, or 0xFFFFFFFF if
|
|
* unknown (immediate GP0 writes) — skips the dataflow tier.
|
|
* packet_word the actual word (validation key).
|
|
* int_x/int_y the natively parsed 11-bit positions (pre-draw-offset).
|
|
* On DATAFLOW/FALLBACK, *x16/*y16 hold the sub-pixel position (16.16, same
|
|
* coordinate space as the packet halves); *sz is the projected depth or 0.
|
|
* Safeguards applied here: the integer part must match the native parse
|
|
* (truncation agreement) and the tolerance clamp. */
|
|
int pgxp_get_precise_vertex(uint32_t addr, uint32_t packet_word,
|
|
int32_t int_x, int32_t int_y,
|
|
int32_t *x16, int32_t *y16, uint16_t *sz);
|
|
|
|
/* --- observability -------------------------------------------------------- */
|
|
|
|
typedef struct PGXPStats {
|
|
uint64_t lookups; /* pgxp_get_precise_vertex calls */
|
|
uint64_t dataflow_hit;
|
|
uint64_t fallback_hit;
|
|
uint64_t native;
|
|
uint64_t value_mismatch; /* shadow present but wrong word (stale) */
|
|
uint64_t trunc_reject; /* integer part disagreed with native parse */
|
|
uint64_t tolerance_reject;
|
|
uint64_t w_valid; /* lookups that also carried a usable depth */
|
|
uint64_t produced; /* RTPS/RTPT projections pushed into shadows */
|
|
uint64_t swc2_stores; /* GTE reg shadows copied to RAM shadows */
|
|
uint64_t invalidations; /* generation bumps (each kills all shadows) */
|
|
} PGXPStats;
|
|
|
|
void pgxp_get_stats(PGXPStats *out);
|
|
|
|
/* --- gte.cpp forwarding surface (v14 ABI compat) -------------------------- */
|
|
|
|
/* SWC2 site: copy the GTE register shadow (regs 12..15) to the RAM shadow. */
|
|
void pgxp_store_gte_reg(uint32_t addr, uint8_t reg);
|
|
|
|
/* Address-keyed precise-word lookup (perspective texturing path). Returns
|
|
* nonzero when the tracked word matches `packed` AND carries a depth. */
|
|
int pgxp_debug_shadow_class(uint32_t addr, uint32_t packed);
|
|
int pgxp_load_precise_word(uint32_t addr, uint32_t packed,
|
|
int32_t *x16, int32_t *y16, uint16_t *z);
|
|
|
|
/* --- test accessors (always compiled; trivial) ---------------------------- */
|
|
|
|
/* index 0..3 selects the SXY0..SXYP register shadow (GTE data regs 12..15). */
|
|
void pgxp_test_seed_gte_sxy(uint32_t index, uint32_t packed,
|
|
int32_t x16, int32_t y16, uint16_t z, int valid);
|
|
void pgxp_test_get_gte_sxy(uint32_t index, uint32_t *packed,
|
|
int32_t *x16, int32_t *y16, uint16_t *z,
|
|
uint8_t *valid);
|
|
uint32_t pgxp_test_generation(void);
|
|
uint32_t pgxp_test_suppress_depth(void);
|
|
int pgxp_test_active(void);
|
|
void pgxp_test_set_generation(uint32_t gen);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* PGXP_H */
|