mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
overlay: unify cache read/write location + fix missing ws_cull symbol in DLLs
Two framework bugs that made overlay variants run interpreted forever (the "agonizing lag" in overlay-heavy areas like Tomba's dwarf village): 1. Cache read/write divergence. The loader reads <exe>/cache and the runtime writes captures to <exe>/overlay_captures.json, but the gcc autocompile path used the game.toml command verbatim, whose hard-coded --out-dir/--captures pointed elsewhere (build-stable/build-modern). So compiled shards landed where the loader never looked and captures were read stale -> the variant never went native. Fix: the runtime now injects PSX_OVERLAY_CACHE_DIR / PSX_OVERLAY_CAPTURES (the canonical loader paths) into every autocompile spawn; compile_overlays.py honors them over any CLI flag (--captures now optional). One source of truth for the cache location, uniform across all games, dev or prod. gcc/tcc stay separated by the existing <compiler> namespace. 2. Overlay DLL preamble missing psx_ws_cull_sltiu. auto_screen_x emits this helper at every width-cull site into EVERY build's C (identity at 4:3), and the main EXE links it from gpu.c, but the overlay DLL preamble never defined it -> any overlay variant with cull sites failed to link -> no shard -> interpreter. Fix: provide the self-contained helper (depends only on the already-forwarded psx_ws_x_margin) in the DLL preamble. Proven on Tomba dwarf village: dispatch_native 29/s -> 11454/s (was ~97% interp, now ~97% native). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,16 @@ extern "C" {
|
||||
void autocompile_configure(const char *cmd, const char *cwd);
|
||||
int autocompile_configured(void);
|
||||
|
||||
/* Pin the canonical overlay cache dir + captures file the LOADER actually uses
|
||||
* (<exe>/cache and <exe>/overlay_captures.json — computed once in main.cpp).
|
||||
* autocompile injects these into every spawned compile via the environment
|
||||
* (PSX_OVERLAY_CACHE_DIR / PSX_OVERLAY_CAPTURES), so the WRITE cache and the
|
||||
* READ captures can never diverge from where the loader reads — for every game,
|
||||
* dev or prod, gcc or tcc. compile_overlays.py / coverage_vault.py honor these
|
||||
* over any CLI --out-dir/--captures. This is the single source of truth for the
|
||||
* cache location: no per-game config, no drift. */
|
||||
void autocompile_set_cache_paths(const char *cache_dir, const char *captures);
|
||||
|
||||
/* Probe whether a C compiler is actually reachable on PATH (gcc/cc/clang) — the
|
||||
* REAL "developer machine" signal, distinct from autocompile_configured() which
|
||||
* only reports that a command STRING is set (the shipped game.toml always sets
|
||||
|
||||
@@ -17,6 +17,11 @@ extern void overlay_loader_rescan(void);
|
||||
static char s_cmd[4096]; /* large: the runtime-constructed bundled tcc cmd has
|
||||
* many absolute paths (python+script+recompiler+tcc+...) */
|
||||
static char s_cwd[512];
|
||||
/* Canonical loader cache dir + captures file (see autocompile_set_cache_paths).
|
||||
* Injected into the compile child's environment so its WRITE location is exactly
|
||||
* the loader's READ location, regardless of what the configured command says. */
|
||||
static char s_cache_dir[512];
|
||||
static char s_captures[512];
|
||||
|
||||
enum { AC_IDLE = 0, AC_RUNNING = 1, AC_DONE = 2 };
|
||||
static volatile int s_state = AC_IDLE;
|
||||
@@ -84,6 +89,11 @@ void autocompile_configure(const char *cmd, const char *cwd) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void autocompile_set_cache_paths(const char *cache_dir, const char *captures) {
|
||||
snprintf(s_cache_dir, sizeof(s_cache_dir), "%s", cache_dir ? cache_dir : "");
|
||||
snprintf(s_captures, sizeof(s_captures), "%s", captures ? captures : "");
|
||||
}
|
||||
|
||||
int autocompile_configured(void) { return s_cmd[0] != '\0'; }
|
||||
int autocompile_busy(void) { return s_state == AC_RUNNING; }
|
||||
|
||||
@@ -133,6 +143,14 @@ int autocompile_request(void) {
|
||||
if (!CreatePipe(&rd, &wr, &sa, 0)) return 0;
|
||||
SetHandleInformation(rd, HANDLE_FLAG_INHERIT, 0);
|
||||
|
||||
/* Pin the WRITE cache + READ captures to the loader's canonical locations.
|
||||
* Set on the PARENT env so the child (CreateProcessA lpEnvironment=NULL =>
|
||||
* inherit) sees them; compile_overlays.py / coverage_vault.py prefer these
|
||||
* over any CLI --out-dir/--captures, so the cache can never drift from where
|
||||
* the loader reads. This is THE fix for the read/write-location divergence. */
|
||||
if (s_cache_dir[0]) SetEnvironmentVariableA("PSX_OVERLAY_CACHE_DIR", s_cache_dir);
|
||||
if (s_captures[0]) SetEnvironmentVariableA("PSX_OVERLAY_CAPTURES", s_captures);
|
||||
|
||||
/* cmd.exe /C resolves the command via PATH and supports the relative
|
||||
* paths in the configured line (cwd = project root). */
|
||||
char full[4200];
|
||||
|
||||
+12
-2
@@ -2039,11 +2039,21 @@ int main(int argc, char** argv) {
|
||||
ac_cmd = &gc.runtime.overlay_autocompile_cmd;
|
||||
}
|
||||
if (ac_cmd) {
|
||||
/* Pin the compile's WRITE cache + READ captures to the SAME
|
||||
* canonical locations the loader uses (cache_dir = <exe>/cache,
|
||||
* <exe>/overlay_captures.json — set above). The framework owns
|
||||
* the cache location; no game.toml --out-dir/--captures can make
|
||||
* the write drift from the read. Single source of truth, all
|
||||
* games, dev or prod. */
|
||||
std::string captures_path =
|
||||
(exe_dir / "overlay_captures.json").string();
|
||||
autocompile_set_cache_paths(cache_dir.c_str(),
|
||||
captures_path.c_str());
|
||||
autocompile_configure(ac_cmd->c_str(),
|
||||
gc.project_root.string().c_str());
|
||||
std::fprintf(stdout,
|
||||
"psxrecomp: overlay autocompile enabled (%s)\n",
|
||||
overlay_backend_name(eff));
|
||||
"psxrecomp: overlay autocompile enabled (%s); cache=%s\n",
|
||||
overlay_backend_name(eff), cache_dir.c_str());
|
||||
}
|
||||
code_provider_init(cfg_backend, gcc_avail);
|
||||
/* Now that the backend is resolved, apply the sljit live policy:
|
||||
|
||||
@@ -853,6 +853,18 @@ int psx_ws_backdrop_x(int x) {
|
||||
int psx_ws_x_margin(void) {
|
||||
return g_cbs.ws_x_margin ? g_cbs.ws_x_margin() : 0;
|
||||
}
|
||||
/* Shared render-funnel screen-X cull widening ([widescreen.cull] auto_screen_x).
|
||||
* The recompiler emits `rt = psx_ws_cull_sltiu(sx, imm)` at every width-compare
|
||||
* cull site, INCLUDING in overlay-resident code, so the overlay DLL must define
|
||||
* it or it fails to link (undefined reference) -> no native shard -> that overlay
|
||||
* variant runs interpreted forever (the dwarf-village-style lag). Self-contained:
|
||||
* depends only on psx_ws_x_margin() above. Identity at 4:3 (margin 0). MUST stay
|
||||
* byte-for-byte identical to gpu.c psx_ws_cull_sltiu. */
|
||||
int psx_ws_cull_sltiu(uint32_t sx, uint32_t imm) {
|
||||
int m = psx_ws_x_margin();
|
||||
return ((uint32_t)((int32_t)(int16_t)(uint16_t)sx + m)
|
||||
< (uint32_t)((int32_t)imm + 2 * m)) ? 1 : 0;
|
||||
}
|
||||
void psx_ws_sprite_tag(CPUState *cpu) {
|
||||
if (g_cbs.ws_sprite_tag) g_cbs.ws_sprite_tag(cpu);
|
||||
}
|
||||
@@ -1210,8 +1222,11 @@ def compile_dll(c_path: str, out_dll: str, include_dirs: list[str],
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument('--captures', required=True,
|
||||
help='overlay_captures.json from the runtime')
|
||||
ap.add_argument('--captures', default=None,
|
||||
help='overlay_captures.json from the runtime. Optional: the '
|
||||
'runtime injects PSX_OVERLAY_CAPTURES (the canonical '
|
||||
'<exe>/overlay_captures.json), which wins. Required only '
|
||||
'for manual/offline invocation.')
|
||||
ap.add_argument('--game-toml', required=True,
|
||||
help='game.toml (for game_id)')
|
||||
ap.add_argument('--recompiler', required=True,
|
||||
@@ -1242,6 +1257,27 @@ def main():
|
||||
'+ carry an entry-switch. Must match the runtime build.')
|
||||
args = ap.parse_args()
|
||||
|
||||
# ---- Framework-injected cache location wins over CLI flags ----------------
|
||||
# The runtime (autocompile.c) exports PSX_OVERLAY_CACHE_DIR / PSX_OVERLAY_CAPTURES
|
||||
# set to the loader's CANONICAL <exe>/cache and <exe>/overlay_captures.json. We
|
||||
# honor those above --out-dir/--captures so the WRITE cache and READ captures
|
||||
# are always exactly where the loader reads — no per-game config, no drift
|
||||
# between dev and prod (the read/write-location divergence bug). The CLI flags
|
||||
# remain the fallback for manual/offline invocation (no env set).
|
||||
_env_out = os.environ.get('PSX_OVERLAY_CACHE_DIR')
|
||||
if _env_out:
|
||||
if _env_out != args.out_dir:
|
||||
print(f'[cache] PSX_OVERLAY_CACHE_DIR overrides --out-dir: {_env_out}')
|
||||
args.out_dir = _env_out
|
||||
_env_cap = os.environ.get('PSX_OVERLAY_CAPTURES')
|
||||
if _env_cap:
|
||||
if _env_cap != args.captures:
|
||||
print(f'[cache] PSX_OVERLAY_CAPTURES overrides --captures: {_env_cap}')
|
||||
args.captures = _env_cap
|
||||
if not args.captures:
|
||||
ap.error('no captures file: set PSX_OVERLAY_CAPTURES (runtime injects it) '
|
||||
'or pass --captures for manual/offline use')
|
||||
|
||||
# Resolve the recompiler exe and runtime-include to absolute paths up front.
|
||||
# game.toml's overlay_autocompile_cmd uses paths RELATIVE to the project root
|
||||
# (the runtime spawns this script via cmd.exe with cwd = project root) so the
|
||||
|
||||
Reference in New Issue
Block a user