Files
psxrecomp/overlay_toolchain/include/latency_ring.h
Matthew Stanley fe42da2744 Merge release/native-rendering-20261003 into master
Keeps both lines: master's PGXP session, draw-distance clamps, masked rejects
and saturated-winding NCLIP; the release's render-pass kit additions, verified
media/disc extents, timeline/present rings, cull-scale sites. Overlay ABI v30.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 19:27:02 -07:00

69 lines
2.9 KiB
C

#ifndef PSX_LATENCY_RING_H
#define PSX_LATENCY_RING_H
/* Always-on input->photon latency ring.
*
* Every present cycle (one sdl_vblank_present call) stamps a QPC timestamp at
* a few fixed stages. The ring records continuously from process start so a
* probe can QUERY a window of recent frames after the fact (never arm-then-
* capture). It is backend-agnostic: the GL and Vulkan present paths both mark
* the same SWAP_BEGIN/SWAP_END boundary, so their latency is directly
* comparable. Query through the debug server ("latency" command).
*
* Single writer (the emu/vblank thread) marks; the debug-server IO thread reads
* best-effort. Timestamps are monotonic and a torn read only blurs one frame's
* diagnostic, so no locking is used (matches the other always-on rings). */
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
LAT_INPUT = 0, /* pad state sampled into SIO for this cycle */
LAT_PACED, /* frame_pacer_wait() returned (wall-clock cap done) */
LAT_SWAP_BEGIN, /* immediately before the backend present/swap */
LAT_SWAP_END, /* immediately after the present/swap returned */
LAT_STAGE_COUNT
} LatencyStage;
/* Begin a new frame slot and stamp LAT_INPUT. Call once per cycle, at the
* point input is sampled. Advances the ring head. */
void latency_ring_frame_begin(void);
/* Stamp the current frame slot at `stage` (no-op for LAT_INPUT — use
* latency_ring_frame_begin for that). */
void latency_ring_mark(LatencyStage stage);
/* Re-stamp the current slot's LAT_INPUT to now. Used by the low-latency
* present path, which re-samples the pad AFTER the pacer wait: the input the
* CPU actually reads is this later one, so input_to_swap must measure from it.
* (With this active, input_to_paced is negative and drops out of the summary.) */
void latency_ring_restamp_input(void);
/* Note which present backend produced the most recent swaps ("opengl",
* "software", "vulkan"). Reported in the summary so a comparison is labelled. */
void latency_ring_set_backend(const char *name);
/* Note the effective present mode / swap interval for the summary label
* (e.g. 1 = vsync, 0 = immediate, -1 = adaptive; other backends pass their
* own code). Purely descriptive. */
void latency_ring_set_present_mode(int mode);
/* JSON summary over the last `window` completed frames (clamped): mean/min/max/
* p50/p95 in microseconds of frame_period, input->swap, and swap_block (the
* present/swap blocking time — the key vsync/queue-latency signal). Writes a
* brace-delimited object into buf; returns bytes written (excluding NUL). */
int latency_ring_summary_json(char *buf, int buf_size, int window);
/* JSON array of the last `max_frames` raw frame records (timestamps in
* microseconds relative to the oldest reported frame). */
int latency_ring_dump_json(char *buf, int buf_size, int max_frames);
#ifdef __cplusplus
}
#endif
#endif /* PSX_LATENCY_RING_H */