Include fixed scaled-charge batching profile in netplay compatibility

This commit is contained in:
Matthew Stanley
2026-10-14 23:51:49 -07:00
parent 7fe0414dc0
commit dfd4555d09
4 changed files with 22 additions and 3 deletions
+6
View File
@@ -983,6 +983,12 @@ race) agree on `cyc`, `mmio` and `mc` on every frame. They also end with
identical ordered write hashes. The only differences are one-frame VBlank
straddles of `wc`/`sc`, as documented for batched code.
The scale and selected batching profile are fixed at session startup and
included in the existing netplay compatibility fingerprint. Peers with
different batching choices cannot start together. Both paths use portable C;
this does not authorize compatibility between otherwise different builds or
replace the existing game/core/config identity checks.
Gates (all present gates must be open; with none the scale always applies):
- `guest_cycle_scale_gate` — RAM predicates on aligned main-RAM words
+7
View File
@@ -124,6 +124,13 @@ static inline uint32_t psx_gcs_recip_for(uint32_t scale) {
}
/* [timing] guest_cycle_scale_batch: batch scaled CPU charges (psx_cyc.h). */
extern int g_psx_gcs_batch;
/* Fixed session timing profile. Both implementations are portable C and are
* present in every build; peers must agree on the selected one before frame 0.
* Preserve the existing scale-only tag for the unbatched path. */
static inline uint32_t psx_gcs_session_profile(uint32_t scale, int batch) {
if (scale <= 1u) return 0u;
return 0x6C000000u | (batch ? 0x00800000u : 0u) | scale;
}
static inline uint32_t psx_gcs_scale(uint32_t cpu_cycles) {
uint64_t t = (uint64_t)cpu_cycles * g_psx_gcs_recip_q16 + g_psx_gcs_frac;
g_psx_gcs_frac = (uint32_t)(t & 0xFFFFu);
+4 -3
View File
@@ -19631,7 +19631,8 @@ session_reboot:
// exactly as before.
if (psx_guest_cycle_scale_config() != 1u) {
char tag[9];
std::snprintf(tag, sizeof tag, "%08x", 0x6C000000u | psx_guest_cycle_scale_config());
std::snprintf(tag, sizeof tag, "%08x",
psx_gcs_session_profile(psx_guest_cycle_scale_config(), g_psx_gcs_batch));
if (std::strlen(net_cfg.content_fingerprint) != 64)
std::snprintf(net_cfg.content_fingerprint, sizeof(net_cfg.content_fingerprint),
"%s", "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c00000000");
@@ -19640,8 +19641,8 @@ session_reboot:
auto hv = [](char c) { return c <= '9' ? c - '0' : (c | 0x20) - 'a' + 10; };
*d = "0123456789abcdef"[hv(*d) ^ hv(tag[i])];
}
std::printf("psxrecomp: netplay requires guest_cycle_scale %u on every peer\n",
psx_guest_cycle_scale_config());
std::printf("psxrecomp: netplay requires guest_cycle_scale %u, batch profile %d on every peer\n",
psx_guest_cycle_scale_config(), g_psx_gcs_batch != 0);
}
const int nrc = psx_netplay_start(&net_cfg);
if (nrc != 0) {
@@ -129,6 +129,11 @@ static void check_batch_ignores_host_stack(uint32_t scale, uint32_t charges, uin
}
int main(void) {
assert(psx_gcs_session_profile(1, 0) == 0u);
assert(psx_gcs_session_profile(1, 1) == 0u); /* no scaled path */
assert(psx_gcs_session_profile(2, 0) == (0x6C000000u | 2u));
assert(psx_gcs_session_profile(2, 1) != psx_gcs_session_profile(2, 0));
assert(psx_gcs_session_profile(3, 1) != psx_gcs_session_profile(2, 1));
assert(psx_gcs_recip_for(1) == 65536u);
assert(psx_gcs_recip_for(0) == 65536u); /* never slower than faithful */
assert(psx_gcs_recip_for(2) == 32768u);