mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-28 10:59:16 -04:00
2641f7c59b
Framework fixes surfaced while bringing up a title that stresses overlays, HD texture replacement and PGXP at a doubled CRTC rate. All are general; the title's own configuration stays in its game repository. CORRECTNESS * Overlay DLL shim was deleted by the preprocessor. compile_overlays.py injected overlay_dispatch_preamble.c.inc after the syntactically LAST #include. A conditional i-cache include moved that anchor inside an #else, and DLL builds define PSX_OVERLAY_DLL_BUILD, so the callback definitions preprocessed away and every overlay DLL failed to link. Any title using overlays would silently fall back to per-instruction interpretation. The shim is now anchored to the unconditional #include "psx_runtime.h" and raises if that anchor is absent. * Overlay capture kept stale PC bitmaps when executed code later became data, so one variant's PC set could be compiled against another variant's bytes. overlay_watch_note_write now retires the whole page's execution/dispatch epoch when any executed word is overwritten; static demand is keyed by (load_addr, size, bytes) and resolved only against matching range identities. * VRAM-to-VRAM copies only invalidated the destination and never registered it as a replacement candidate, so textures arriving by copy could not be dumped or replaced at all. gr_copy_rect now calls tex_pack_on_copy with the same (texture,palette) key semantics as CPU uploads. * PGXP shared-edge seams: a per-primitive all-or-nothing gate cannot stop two adjacent primitives disagreeing about a shared endpoint, so the edge is submitted at two sub-pixel positions. Adds a frame-stamped canonical endpoint cache keyed by final integer screen position: the first use fixes the coordinates and every later occurrence reuses them. * PGXP provenance: PGXPValue carries per-component X/Y/Z projection ids, so a value assembled from different projections can no longer arm correction. Destructive AND/XOR/NOR/SLT, link and COP0 writes now invalidate shadows, and the SXYP shadow FIFO shifts with guest SXY. * Precise NCLIP no longer replaces guest MAC0. It is telemetry only, so face visibility stays bit-exact with the integer GTE. * Removed two counters that were reported as correctness evidence but were never incremented anywhere. PERFORMANCE * PSX_ENABLE_LTO plus a noinline guard on the shared cycle wrappers. Unguarded ThinLTO clones hot interlock bodies and REGRESSES the hot path; guarded, it becomes a win. Adds runtime/tests/bench_psx_runtime_hotpath.c, which verifies variant equivalence by digest so codegen changes are measurable. * Cycle accounting: emitters select specialized arity-0/1/2/3 helpers at generation time instead of a generic dependency-mask loop; main-RAM loads combine base/wait/completion into one exact charge. Exactness is covered by pipeline-state, load-state, deadline-crossing and cross-device causality tests. * HD texture invalidation replaced a linear scan of up to 8192 upload records per VRAM write with a tile-refcount index; out-of-domain writes early-out. * Translation/text-substitution dispatch is gated behind a monotonic mutation epoch instead of running a callback on every dispatch. * card_data_writes_check no longer runs on every RAM write. CAPABILITY * fixed_outer_aspect: the outer presentation aspect becomes a function of user configuration only. Scene classification selects content transforms, never the outer viewport, so menus/FMV stay centred undistorted inside a wide canvas. VERIFICATION AND LIMITS docs/UPSTREAM_2026-08-20.md audits each change against the diff, marks unsupported claims UNKNOWN, and lists a suggested PR split and review focus. Unit and structural tests accompany each item, but this body of work has NOT been validated end-to-end against a fixed reproducible workload -- reviewers should treat the timing-exactness and PGXP changes as the highest-risk areas. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
80 lines
2.8 KiB
C++
80 lines
2.8 KiB
C++
#include "tex_pack_rect_index.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <vector>
|
|
|
|
using PSXRecomp::TexPackDetail::RectTileIndex;
|
|
|
|
struct Rect { int x, y, w, h; };
|
|
|
|
static bool overlaps(const Rect &a, const Rect &b) {
|
|
return a.x < b.x + b.w && b.x < a.x + a.w &&
|
|
a.y < b.y + b.h && b.y < a.y + a.h;
|
|
}
|
|
|
|
static uint32_t rng_state = 0xC001CAFEu;
|
|
static uint32_t next_random() {
|
|
rng_state ^= rng_state << 13;
|
|
rng_state ^= rng_state >> 17;
|
|
rng_state ^= rng_state << 5;
|
|
return rng_state;
|
|
}
|
|
|
|
static int fail(const char *what, int iteration) {
|
|
std::fprintf(stderr, "tex_pack_rect_index_test: %s at iteration %d\n",
|
|
what, iteration);
|
|
return 1;
|
|
}
|
|
|
|
int main() {
|
|
RectTileIndex index;
|
|
const Rect aligned{32, 48, 16, 16};
|
|
index.add(aligned.x, aligned.y, aligned.w, aligned.h);
|
|
if (!index.maybe_overlaps(32, 48, 1, 1)) return fail("missed occupied tile", 0);
|
|
if (index.maybe_overlaps(16, 48, 16, 16)) return fail("crossed tile boundary", 0);
|
|
index.remove(aligned.x, aligned.y, aligned.w, aligned.h);
|
|
if (index.maybe_overlaps(32, 48, 1, 1)) return fail("remove left occupancy", 0);
|
|
|
|
/* Unknown coordinate domains must degrade to a scan, never a false miss. */
|
|
index.add(4096, 4096, 8, 8);
|
|
if (!index.maybe_overlaps(0, 0, 1, 1)) return fail("unindexed rect was hidden", 0);
|
|
index.remove(4096, 4096, 8, 8);
|
|
if (index.maybe_overlaps(0, 0, 1, 1)) return fail("unindexed remove stuck", 0);
|
|
|
|
std::vector<Rect> live;
|
|
for (int iteration = 1; iteration <= 20000; ++iteration) {
|
|
if (live.empty() || (next_random() & 3u) != 0) {
|
|
Rect r;
|
|
r.x = static_cast<int>(next_random() % 1985u);
|
|
r.y = static_cast<int>(next_random() % 961u);
|
|
r.w = 1 + static_cast<int>(next_random() % 64u);
|
|
r.h = 1 + static_cast<int>(next_random() % 64u);
|
|
live.push_back(r);
|
|
index.add(r.x, r.y, r.w, r.h);
|
|
} else {
|
|
const size_t victim = next_random() % live.size();
|
|
const Rect r = live[victim];
|
|
index.remove(r.x, r.y, r.w, r.h);
|
|
live.erase(live.begin() + static_cast<std::ptrdiff_t>(victim));
|
|
}
|
|
|
|
Rect q;
|
|
q.x = static_cast<int>(next_random() % 1985u);
|
|
q.y = static_cast<int>(next_random() % 961u);
|
|
q.w = 1 + static_cast<int>(next_random() % 64u);
|
|
q.h = 1 + static_cast<int>(next_random() % 64u);
|
|
bool exact = false;
|
|
for (const Rect &r : live) {
|
|
if (overlaps(r, q)) { exact = true; break; }
|
|
}
|
|
if (exact && !index.maybe_overlaps(q.x, q.y, q.w, q.h))
|
|
return fail("false-negative overlap", iteration);
|
|
}
|
|
|
|
index.clear();
|
|
if (index.maybe_overlaps(0, 0, 2048, 1024)) return fail("clear failed", 20001);
|
|
return 0;
|
|
}
|