mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
Preserve PGXP projection and depth through flagged subdivision stores
This commit is contained in:
@@ -280,6 +280,13 @@ void pgxp_note_nclip(int disagree, int corrected);
|
||||
|
||||
/* 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);
|
||||
/* After a title's original store: retain a projected SXY's precision/depth
|
||||
* when only clip/outcode bits outside the GPU's two 11-bit fields changed.
|
||||
* The caller supplies the current GTE word and actual stored word. Changed
|
||||
* coordinates, clamping, stale sources and unmapped destinations fail closed.
|
||||
* Host shadow only; no guest memory/register write. */
|
||||
int pgxp_store_flagged_gte_sxy(uint32_t addr, uint8_t reg,
|
||||
uint32_t source_word, uint32_t stored_word);
|
||||
|
||||
/* Address-keyed precise-word lookup (perspective texturing path). Returns
|
||||
* nonzero when the tracked word matches `packed` AND carries a depth. */
|
||||
|
||||
+38
-4
@@ -999,6 +999,17 @@ static inline int half_exact(const PGXPValue *pv, uint32_t value, int hi_half) {
|
||||
}
|
||||
/* A saturated projection shadow sits beyond the clamped word; it is not
|
||||
* the value of that half and must not be carried as one. */
|
||||
if (s_preserve_projection) {
|
||||
const int32_t field = (int32_t)((uint32_t)half << 21) >> 21;
|
||||
const int64_t d = (int64_t)v16 - (int64_t)half * 65536;
|
||||
// Clip metadata uses bits 13..15. Bits 11/12 must still be the
|
||||
// coordinate's sign fill; a real wrapped coordinate stays strict.
|
||||
const uint16_t sign_fill = field < 0 ? 0x1800u : 0;
|
||||
if (field > -0x400 && field < 0x3FF &&
|
||||
(uint16_t(half) & 0x1800u) == sign_fill)
|
||||
return d > -(int64_t)PGXP_PPP_AGREE_BELOW * 65536 &&
|
||||
d < (int64_t)PGXP_PPP_AGREE_ABOVE * 65536;
|
||||
}
|
||||
return (v16 >> 16) == half;
|
||||
}
|
||||
|
||||
@@ -1646,9 +1657,9 @@ extern "C" void pgxp_gte_reg_written(int reg, uint32_t value) {
|
||||
* packet half, `half` the raw 16-bit half in the word. Exact (integer part
|
||||
* equals the native parse) unless preserve-projection is on; then the precise
|
||||
* position may sit a bounded distance from it (PGXP_PPP_AGREE_*), except
|
||||
* when the half is at or beyond the GTE saturation limits: there the guest
|
||||
* integer is a clamp that says nothing about the vertex, or (beyond them) a
|
||||
* CPU-modified word the GPU's 11-bit parse wraps. */
|
||||
* when the decoded coordinate is saturated or the word is a genuinely
|
||||
* wrapped coordinate. Known outcode bits 13..15 alone are not saturation;
|
||||
* bits 11/12 must still match the decoded coordinate's sign fill. */
|
||||
static inline int pgxp_agrees(int32_t p16, int32_t native, int16_t half,
|
||||
int derived) {
|
||||
const int64_t d = (int64_t)p16 - (int64_t)native * 65536;
|
||||
@@ -1656,7 +1667,9 @@ static inline int pgxp_agrees(int32_t p16, int32_t native, int16_t half,
|
||||
* own arithmetic chose, so the value may sit one pixel either side of
|
||||
* the truncation window - still bounded, still the same vertex. */
|
||||
if (derived) return d > -65536 && d < 2 * 65536;
|
||||
if (!s_preserve_projection || half <= -0x400 || half >= 0x3FF)
|
||||
const uint16_t sign_fill = native < 0 ? 0x1800u : 0;
|
||||
if (!s_preserve_projection || native <= -0x400 || native >= 0x3FF ||
|
||||
(uint16_t(half) & 0x1800u) != sign_fill)
|
||||
return (p16 >> 16) == native;
|
||||
return d > -(int64_t)PGXP_PPP_AGREE_BELOW * 65536 &&
|
||||
d < (int64_t)PGXP_PPP_AGREE_ABOVE * 65536;
|
||||
@@ -1815,6 +1828,27 @@ extern "C" void pgxp_store_gte_reg(uint32_t addr, uint8_t reg) {
|
||||
*dst = *src;
|
||||
}
|
||||
|
||||
extern "C" int pgxp_store_flagged_gte_sxy(uint32_t addr, uint8_t reg,
|
||||
uint32_t source_word, uint32_t stored_word) {
|
||||
if (!g_pgxp_active || (addr & 3u) || reg < 12 || reg > 15 ||
|
||||
((source_word ^ stored_word) & 0x07FF07FFu)) return 0;
|
||||
const PGXPValue* src = &s_gte[reg];
|
||||
if (src->gen != s_gen || src->value != source_word || !src->z ||
|
||||
(src->flags & (PGXP_F_VXY | PGXP_F_VZ)) != (PGXP_F_VXY | PGXP_F_VZ)) return 0;
|
||||
const int64_t x = (int64_t)src->x16 +
|
||||
((int64_t)(int16_t)stored_word - (int16_t)source_word) * 65536;
|
||||
const int64_t y = (int64_t)src->y16 +
|
||||
((int64_t)(int16_t)(stored_word >> 16) - (int16_t)(source_word >> 16)) * 65536;
|
||||
if (x < INT32_MIN || x > INT32_MAX || y < INT32_MIN || y > INT32_MAX) return 0;
|
||||
PGXPValue* dst = pgxp_ptr_w(addr);
|
||||
if (!dst) return 0;
|
||||
*dst = *src;
|
||||
dst->value = stored_word;
|
||||
dst->x16 = (int32_t)x;
|
||||
dst->y16 = (int32_t)y;
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" int pgxp_debug_shadow(int space, uint32_t key, int *live,
|
||||
uint32_t *value, uint32_t *flags,
|
||||
int32_t *x16, int32_t *y16, uint16_t *z) {
|
||||
|
||||
@@ -239,12 +239,16 @@ int main(void) {
|
||||
* The flag ops rewrite bits 14/15 and 30/31 of the word, never the 11-bit
|
||||
* fields GP0 decodes, so the vertex (and its depth) must survive - in
|
||||
* BOTH tiers, since bitwise carries are exact. --- */
|
||||
for (int mode = 0; mode < 2; mode++) {
|
||||
for (int exact = 0; exact < 2; exact++) {
|
||||
pgxp_set_preserve_projection(exact);
|
||||
const int32_t expected_x = X16 - (exact ? 65536 : 0);
|
||||
const int32_t expected_y = Y16 + (exact ? 65536 : 0);
|
||||
for (int mode = 0; mode < 2; mode++) {
|
||||
pgxp_set_cpu_mode(mode);
|
||||
const uint32_t T2 = 10, T4 = 12, T5 = 13, T9 = 25, A3 = 7;
|
||||
const uint32_t bounds = (240u << 16) | 128u; /* x >= 128 -> flag */
|
||||
const uint32_t m14 = 0xBFFFBFFFu, m15 = 0x7FFF7FFFu;
|
||||
pgxp_gte_push_sxy(X16, Y16, SZ3, PACKED);
|
||||
pgxp_gte_push_sxy(expected_x, expected_y, SZ3, PACKED);
|
||||
psx_pgxp_cop2(nullptr, MFC2(T2, 14), PACKED, 0);
|
||||
psx_pgxp_load(nullptr, LW(1, T4), 0x80180000u, bounds); /* untracked */
|
||||
psx_pgxp_alu(nullptr, LUI(T5, 0xBFFF), 0xBFFF0000u, 0, 0);
|
||||
@@ -266,11 +270,13 @@ int main(void) {
|
||||
psx_pgxp_store(nullptr, SW(1, T2), ADDR_B, w);
|
||||
int32_t x, y; uint16_t z;
|
||||
CHECK(lookup(ADDR_B, w, 160, 80, &x, &y, &z) == PGXP_SRC_DATAFLOW);
|
||||
CHECK(x == X16 && y == Y16 && z == SZ3);
|
||||
CHECK(x == expected_x && y == expected_y && z == SZ3);
|
||||
uint16_t wz = 0;
|
||||
CHECK(pgxp_load_precise_word(ADDR_B, w, nullptr, nullptr, &wz) == 1);
|
||||
CHECK(wz == SZ3);
|
||||
}
|
||||
}
|
||||
pgxp_set_preserve_projection(0);
|
||||
pgxp_set_cpu_mode(0);
|
||||
|
||||
/* --- quad from a projected-vertex table (Spider-Man 0x8007C5D4):
|
||||
|
||||
@@ -188,6 +188,31 @@ int main() {
|
||||
check_attributes(arena, capture(Arena, Packed));
|
||||
pgxp_invalidate_all();
|
||||
CHECK(!capture(Arena, Packed).valid);
|
||||
// Neversoft subdivision unpacks/re-packs SXY and adds screen outcodes.
|
||||
// Reattach validated position/depth; exact projection may cross the
|
||||
// original integer boundary while the guest packet remains unchanged.
|
||||
pgxp_set_preserve_projection(1);
|
||||
pgxp_set_tolerance(-1.0f);
|
||||
const uint32_t Flagged = Packed | 0x80004000u;
|
||||
pgxp_gte_push_sxy((8<<16)-16384, (12<<16)+73728, 300, Packed);
|
||||
CHECK(pgxp_store_flagged_gte_sxy(Arena, 14, Packed, Flagged));
|
||||
int32_t precise_x=0, precise_y=0;
|
||||
CHECK(pgxp_get_precise_vertex(Arena, Flagged, 8, 12, &precise_x, &precise_y, &depth) == PGXP_SRC_DATAFLOW);
|
||||
CHECK(precise_x == (8<<16)-16384 && precise_y == (12<<16)+73728 && depth == 300);
|
||||
CHECK(pgxp_load_precise_word(Arena, Flagged, nullptr, nullptr, &depth) && depth == 300);
|
||||
const auto flagged = capture(Arena, Flagged);
|
||||
CHECK(!pgxp_store_flagged_gte_sxy(Arena, 14, Packed + 1, Flagged));
|
||||
CHECK(!pgxp_store_flagged_gte_sxy(Arena, 14, Packed, Flagged ^ 1));
|
||||
CHECK(!pgxp_store_flagged_gte_sxy(Arena + 1, 14, Packed, Flagged));
|
||||
CHECK(!pgxp_store_flagged_gte_sxy(0x1F801814u, 14, Packed, Flagged));
|
||||
CHECK(!pgxp_store_flagged_gte_sxy(Arena, 11, Packed, Flagged));
|
||||
check_attributes(flagged, capture(Arena, Flagged));
|
||||
pgxp_checkpoint_begin();
|
||||
CHECK(pgxp_store_flagged_gte_sxy(Arena, 14, Packed, Packed));
|
||||
pgxp_checkpoint_rollback();
|
||||
check_attributes(flagged, capture(Arena, Flagged));
|
||||
pgxp_invalidate_all();
|
||||
CHECK(!pgxp_store_flagged_gte_sxy(Arena, 14, Packed, Flagged));
|
||||
gpu_bytes = 0; // pages retained on the host are not guest allocations
|
||||
CHECK(!pgxp_capture_word_shadow(Arena, Packed, &refused));
|
||||
std::printf("PGXP packet shadow checks: %s\n",failures?"FAIL":"PASS");
|
||||
|
||||
Reference in New Issue
Block a user