Files
psxrecomp/runtime/include/render_pass.h
T
Shokunin 1a8f382085 Render passes: put host nesting back after a watchdog abort
The watchdog leaves a runaway pass by longjmp from inside guest code, which
skips the exits of every frame it leaves: generated functions' bb_defer
cleanup, overlay_loader_call_native's unit depth, the native-shard stack and
cycle-flush hook, the DMA kick's exec depth, and the dirty-RAM interpreter's
active/phase/precise flags, resume latch and pending load. The checkpoint
now saves all of it and the restore puts it back (bb_defer with the cycle
freeze), as the scheduler and exception landings do for theirs. An overlay
shard's unpublished pass cycles are flushed into the frozen clock at the
landing so they never reach the live one.

PSX_RENDER_PASS_VERIFY also checks that a pass which returned normally left
the nesting balanced, since the restore would hide that. render_pass_stats
gains nesting_repairs and the fault log names what the landing undid.

render_pass_abort_test aborts a pass from five nested frames (real
render_pass.c and psx_cycles.c, mock devices and presenter) and checks every
value, the clock and RAM; it fails without this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:02:47 -07:00

62 lines
2.7 KiB
C

#ifndef PSX_RENDER_PASS_H
#define PSX_RENDER_PASS_H
/* Host-timed render passes: runtime side of psx_mod_render_pass_plan() and
* psx_mod_render_pass() (mod_plugins.h). See docs/RENDER_PASSES.md. */
#include <stdint.h>
#include "psx_cycle_freeze.h" /* g_psx_render_pass_active, the time freeze */
#ifdef __cplusplus
extern "C" {
#endif
/* Stores a pass may not make (memory.c render_pass_store): they would escape
* the restore, so they are dropped and counted per device class. */
enum {
RENDER_PASS_DROP_SPU = 0,
RENDER_PASS_DROP_CD,
RENDER_PASS_DROP_TIMER,
RENDER_PASS_DROP_DMA, /* DMA channels other than GPU (2) and OTC (6) */
RENDER_PASS_DROP_GPU, /* GP1 commands other than DMA mode / info */
RENDER_PASS_DROP_OTHER, /* SIO, MDEC, memory control, mod arenas, ... */
RENDER_PASS_DROP_CLASSES
};
extern uint64_t g_render_pass_dropped_writes[RENDER_PASS_DROP_CLASSES];
typedef struct RenderPassStats {
uint64_t plans; /* psx_mod_render_pass_plan calls that planned >= 1 */
uint64_t planned; /* phases planned (after shedding) */
uint64_t wanted; /* phases wanted before shedding */
uint64_t refused; /* plan calls that returned 0 while enabled */
uint64_t passes; /* passes run to completion and presented */
uint64_t aborted; /* passes rolled back by a fault */
uint64_t discarded; /* passes whose plugin declined the image */
uint64_t watchdog; /* of which: guest-cycle watchdog overruns */
uint64_t vram_leaks; /* GPU writes outside the declared rect (dropped) */
uint64_t nesting_repairs; /* aborts whose skipped exits the restore undid */
uint64_t verify_checks; /* PSX_RENDER_PASS_VERIFY comparisons */
uint64_t verify_mismatch; /* ... that found a difference */
uint64_t dropped[RENDER_PASS_DROP_CLASSES];
double last_pass_ms; /* host time of the last pass (sandbox + draw) */
double avg_pass_ms; /* smoothed */
double avg_begin_ms; /* smoothed: VRAM backup + checkpoint */
double avg_guest_ms; /* smoothed: the plugin's guest draw code */
double avg_end_ms; /* smoothed: GPU flush, capture, VRAM restore */
double avg_restore_ms; /* smoothed: machine state restore */
uint64_t guest_cycles_last; /* guest cycles the last pass executed */
int disabled; /* sticky: passes disabled after repeated faults */
int watchdog_flag; /* internal: the current pass overran */
} RenderPassStats;
void render_pass_get_stats(RenderPassStats *out);
/* Clears counters and the sticky fault latch (new mod session). */
void render_pass_reset_session(void);
#ifdef __cplusplus
}
#endif
#endif