mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
runtime: cheap SPUCNT gate for the sample-event scheduler
psx_spu_sample_event_service() and psx_spu_sample_event_cycles_to_next() built a full SpuGlobalState snapshot (memset + ~40 register reads + three 24-voice loops) on every device-service call and every deadline recompute, just to test one bit (SPUCNT & 0x40, SPU IRQ9 enable). Under an MMIO-polling guest loop — every PS1 wait loop, loudest on the BoF3 Capcom FMV whose LOGO overlay polls per tiny basic block — the snapshot was ~40% of emu-thread wall time (gdb stack-sampled, clean boot, live FMV). Add spu_ctrl_read() (one array load) and gate on it. Semantics identical: same register, same bit. Measured (BoF3 Capcom FMV, clean boot, headless vblank raises/s): Debug build: 28.5 -> ~52 RelWithDebInfo build: 97.6 uncapped (~1.6x realtime; paced holds 60) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -81,6 +81,11 @@ typedef struct SpuGlobalState {
|
||||
|
||||
void spu_get_voice_state(int voice, SpuVoiceState* out);
|
||||
void spu_get_global_state(SpuGlobalState* out);
|
||||
/* SPUCNT (0x1F801DAA) alone. The sample-event scheduler gates on one ctrl bit
|
||||
* per device-service call; building the full SpuGlobalState there (register
|
||||
* sweep + three 24-voice loops) was ~40% of emu-thread time during the Capcom
|
||||
* FMV's MMIO-polling loop (gdb-sampled 2026-09-01). */
|
||||
uint16_t spu_ctrl_read(void);
|
||||
/* Debug peek into SPU sample RAM (spu_ram TCP command). */
|
||||
uint32_t spu_ram_peek(uint32_t addr, uint8_t *out, uint32_t len);
|
||||
|
||||
|
||||
@@ -418,7 +418,6 @@ static int spu_sample_event_mode(void) {
|
||||
}
|
||||
|
||||
uint32_t psx_spu_sample_event_cycles_to_next(void) {
|
||||
SpuGlobalState state;
|
||||
g_spu_sample_deadline_queries++;
|
||||
if (!spu_sample_event_mode()) {
|
||||
g_spu_sample_mode_rejects++;
|
||||
@@ -428,8 +427,10 @@ uint32_t psx_spu_sample_event_cycles_to_next(void) {
|
||||
g_spu_sample_pump_null_rejects++;
|
||||
return UINT32_MAX;
|
||||
}
|
||||
spu_get_global_state(&state);
|
||||
if ((state.ctrl & 0x0040u) == 0) {
|
||||
/* Gate on SPUCNT alone — this runs on every deadline recompute, and the
|
||||
* full SpuGlobalState snapshot here dominated the emu thread under an
|
||||
* MMIO-polling guest loop (Capcom FMV, gdb-sampled 2026-09-01). */
|
||||
if ((spu_ctrl_read() & 0x0040u) == 0) {
|
||||
g_spu_sample_ctrl_rejects++;
|
||||
return UINT32_MAX;
|
||||
}
|
||||
@@ -449,9 +450,9 @@ void psx_spu_sample_event_service(void) {
|
||||
g_spu_sample_service_checks++;
|
||||
if (!spu_sample_event_mode() || !s_midframe_audio_pump)
|
||||
return;
|
||||
SpuGlobalState state;
|
||||
spu_get_global_state(&state);
|
||||
if ((state.ctrl & 0x0040u) != 0) {
|
||||
/* SPUCNT alone — same hot-gate rationale as the deadline query above. */
|
||||
const uint16_t ctrl = spu_ctrl_read();
|
||||
if ((ctrl & 0x0040u) != 0) {
|
||||
g_spu_sample_enabled_services++;
|
||||
g_spu_sample_last_service_phase = (uint32_t)(psx_cycle_count % 768u);
|
||||
}
|
||||
@@ -460,7 +461,7 @@ void psx_spu_sample_event_service(void) {
|
||||
if ((psx_get_cycle_count() % 768u) != 0)
|
||||
g_spu_sample_deferred_mismatches++;
|
||||
}
|
||||
if ((state.ctrl & 0x0040u) != 0 &&
|
||||
if ((ctrl & 0x0040u) != 0 &&
|
||||
(psx_get_cycle_count() % 768u) == 0) {
|
||||
g_spu_sample_service_pumps++;
|
||||
s_midframe_audio_pump();
|
||||
|
||||
@@ -1601,6 +1601,10 @@ uint32_t spu_ram_peek(uint32_t addr, uint8_t *out, uint32_t len) {
|
||||
return len;
|
||||
}
|
||||
|
||||
uint16_t spu_ctrl_read(void) {
|
||||
return spu_regs[reg_index(0x1F801DAAu)];
|
||||
}
|
||||
|
||||
void spu_get_global_state(SpuGlobalState* out) {
|
||||
if (!out) return;
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
Reference in New Issue
Block a user