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>
This commit is contained in:
Matthew Stanley
2026-10-04 19:27:02 -07:00
487 changed files with 135860 additions and 3332 deletions
+326 -18
View File
@@ -31,6 +31,9 @@
#include "gpu.h"
#include "gpu_render.h" /* gr_scale + gr_render_display_hires (screenshot_hires) */
#include "present_ring.h"
#include "gpu_timeline.h"
#include "gte_nclip_stats.h"
#include "present_image_ring.h"
#include "load_transition_ring.h"
#include "cdrom.h"
#include "sio.h"
@@ -5293,7 +5296,9 @@ static void handle_geom_correction(int id, const char *json)
"\"tri_precise\":%llu,\"tri_mixed\":%llu,\"tri_native\":%llu,"
"\"rect_bypass\":%llu,\"rect_partial\":%llu,"
"\"nclip_precise\":%llu,\"nclip_disagree\":%llu,"
"\"nclip_corrected\":%llu}}",
"\"nclip_corrected\":%llu,"
"\"word\":{\"lookups\":%llu,\"hit\":%llu,\"untracked\":%llu,"
"\"mismatch\":%llu,\"partial\":%llu,\"no_z\":%llu}}}",
id,
gte_geometry_correction_enabled(),
gpu_texture_correction_enabled(),
@@ -5325,7 +5330,81 @@ static void handle_geom_correction(int id, const char *json)
(unsigned long long)ps.rect_partial,
(unsigned long long)ps.nclip_precise,
(unsigned long long)ps.nclip_disagree,
(unsigned long long)ps.nclip_corrected);
(unsigned long long)ps.nclip_corrected,
(unsigned long long)ps.word_lookups,
(unsigned long long)ps.word_hit,
(unsigned long long)ps.word_untracked,
(unsigned long long)ps.word_mismatch,
(unsigned long long)ps.word_partial,
(unsigned long long)ps.word_no_z);
}
/* pgxp_shadow — read PGXP shadow slots: {"cmd":"pgxp_shadow","addr":A,
* "count":N} walks N guest words from A (RAM / scratchpad); "space":"gpr" or
* "gte" with "index"/"count" reads register shadows instead. Each slot: the
* word it describes, whether it is live this generation, the per-half/depth
* flags (1 = X, 2 = Y, 4 = Z) and the 16.16 positions. Pair it with read_ram
* to see whether a packet word still matches its shadow. */
static void handle_pgxp_shadow(int id, const char *json)
{
char sp[16] = "";
json_get_str(json, "space", sp, sizeof sp);
int space = strcmp(sp, "gpr") == 0 ? 1 : strcmp(sp, "gte") == 0 ? 2 : 0;
char addr_str[32] = "";
uint32_t key = space ? (uint32_t)json_get_int(json, "index", 0)
: (json_get_str(json, "addr", addr_str, sizeof addr_str)
? hex_to_u32(addr_str) : 0u);
int count = json_get_int(json, "count", 1);
if (count < 1) count = 1;
if (count > 256) count = 256;
static char buf[256 * 112 + 128];
size_t n = (size_t)snprintf(buf, sizeof buf, "{\"id\":%d,\"ok\":true,\"slots\":[", id);
for (int i = 0; i < count && n < sizeof buf - 128; i++) {
uint32_t k = space ? key + (uint32_t)i : key + (uint32_t)i * 4u;
int live = 0; uint32_t value = 0, flags = 0; int32_t x16 = 0, y16 = 0; uint16_t z = 0;
if (!pgxp_debug_shadow(space, k, &live, &value, &flags, &x16, &y16, &z))
break;
n += (size_t)snprintf(buf + n, sizeof buf - n,
"%s{\"key\":\"0x%08X\",\"live\":%d,\"value\":\"0x%08X\","
"\"flags\":%u,\"x16\":%d,\"y16\":%d,\"z\":%u}",
i ? "," : "", k, live, value, flags, x16, y16, z);
}
snprintf(buf + n, sizeof buf - n, "]}");
send_fmt("%s", buf);
}
/* pgxp_miss_ring — the newest refused perspective-depth lookups (always-on
* ring, PGXP_MISS_RING_CAP deep): {"cmd":"pgxp_miss_ring","count":N}. Each
* entry: packet word address (canonical RAM offset), the word the GPU
* consumed, the word/flags the shadow held, and the reason (untracked,
* mismatch, partial, no_z). Join addr with wtrace_dump to name the writer. */
static void handle_pgxp_miss_ring(int id, const char *json)
{
static const char *const reasons[] = { "?", "untracked", "mismatch",
"partial", "no_z" };
const PGXPWordMiss *ring; uint32_t cap;
uint64_t total = pgxp_word_miss_ring(&ring, &cap);
int count = json_get_int(json, "count", 256);
uint64_t avail = total < cap ? total : cap;
if (count < 0) count = 0;
if ((uint64_t)count > avail) count = (int)avail;
size_t sz = (size_t)count * 160u + 256u;
char *buf = (char *)malloc(sz);
if (!buf) { send_err(id, "oom"); return; }
size_t n = (size_t)snprintf(buf, sz, "{\"id\":%d,\"ok\":true,\"total\":%llu,\"entries\":[",
id, (unsigned long long)total);
for (int i = 0; i < count; i++) {
const PGXPWordMiss *m = &ring[(total - (uint64_t)count + (uint64_t)i) % cap];
n += (size_t)snprintf(buf + n, sz - n,
"%s{\"seq\":%llu,\"addr\":\"0x%08X\",\"packet\":\"0x%08X\","
"\"shadow\":\"0x%08X\",\"flags\":%u,\"live\":%u,\"reason\":\"%s\"}",
i ? "," : "", (unsigned long long)m->seq, m->addr, m->packet,
m->shadow_value, m->shadow_flags, m->live,
reasons[m->reason < 5 ? m->reason : 0]);
}
snprintf(buf + n, sz - n, "]}");
send_fmt("%s", buf);
free(buf);
}
/* pgxp — live-tune the value-propagation engine for one-toggle isolation runs
@@ -7880,6 +7959,33 @@ static void handle_ws_hud_mode(int id, const char *json)
send_fmt("{\"id\":%d,\"ok\":true,\"tag_rects\":%d}", id, v ? 1 : 0);
}
/* Explicit HUD-anchor / background tag pipeline counters (always on):
* {"cmd":"ws_tag_stats"}
* -> hud / background: tag_calls, tag_rejected, hit, stale, guard_mismatch,
* last_tag_frame, last_hit_frame. A title plugin that tags but never hits
* is either tagging the wrong address or rewriting packets after tagging. */
static void handle_ws_tag_stats(int id, const char *json)
{
(void)json;
GpuWsTagStats hud, bg;
gpu_ws_get_tag_stats(&hud, &bg);
const GpuWsTagStats *s[2] = { &hud, &bg };
char part[2][256];
for (int i = 0; i < 2; i++)
snprintf(part[i], sizeof part[i],
"{\"tag_calls\":%llu,\"tag_rejected\":%llu,\"hit\":%llu,"
"\"stale\":%llu,\"guard_mismatch\":%llu,"
"\"last_tag_frame\":%u,\"last_hit_frame\":%u}",
(unsigned long long)s[i]->tag_calls,
(unsigned long long)s[i]->tag_rejected,
(unsigned long long)s[i]->hit,
(unsigned long long)s[i]->stale,
(unsigned long long)s[i]->guard_mismatch,
s[i]->last_tag_frame, s[i]->last_hit_frame);
send_fmt("{\"id\":%d,\"ok\":true,\"frame\":%llu,\"hud\":%s,\"background\":%s}",
id, (unsigned long long)s_frame_count, part[0], part[1]);
}
/* Kernel-image bless state: {"cmd":"kernel_bless"} ->
* entries/clean/mismatch/native_hits/verifies/invalidations, plus the
* declared kernel patch ranges and the segments they made the verifier
@@ -8156,11 +8262,18 @@ static void handle_display_aspect(int id, const char *json) {
int num=json_get_int(json,"num",-1), den=json_get_int(json,"den",-1);
int adaptive=json_get_int(json,"adaptive",0);
if (!psx_debug_display_aspect(num,den,adaptive)) {
send_err(id,"invalid display aspect (4:3 through 32:9)");return;
send_err(id,"invalid display aspect (4:3 or wider, terms 1..99)");return;
}
send_fmt("{\"id\":%d,\"ok\":true,\"num\":%d,\"den\":%d,\"adaptive\":%d}",id,num,den,adaptive!=0);
}
extern int psx_debug_window_size(int w, int h);
static void handle_window_size(int id, const char *json) {
int w = json_get_int(json, "w", -1), h = json_get_int(json, "h", -1);
if (!psx_debug_window_size(w, h)) { send_err(id, "need a window and 64<=w,h<=16384"); return; }
send_fmt("{\"id\":%d,\"ok\":true,\"w\":%d,\"h\":%d}", id, w, h);
}
/* Live native-wide vs squash toggle (A/B): ws_nw on=<0|1> re-engages the wide
* path in the chosen mode without a relaunch. 2 = native-wide, 1 = squash. */
extern void psx_ws_set_native_wide(int on);
@@ -8882,6 +8995,12 @@ typedef struct {
uint16_t *vram; /* full 1024x512 */
} DispRingEntry;
static PSX_BSS DispRingEntry s_disp_ring[DISP_RING_CAP];
/* Capture mark state (debug_server_capture_mark / capture_mark). */
static int s_capture_frozen = 0;
static uint64_t s_capture_mark_frame = 0;
static uint64_t s_capture_mark_count = 0;
static char s_capture_wide_path[512];
static int s_capture_wide_ok = 0;
static uint16_t *s_disp_ring_px = NULL; /* one block for all entries */
static void disp_ring_capture(void)
@@ -8895,7 +9014,7 @@ static void disp_ring_capture(void)
const char *e = getenv("PSX_DISPLAY_RING");
enabled = (!e || !*e || *e != '0') ? 1 : 0;
}
if (!enabled) return;
if (!enabled || s_capture_frozen) return;
if (!s_disp_ring_px) {
size_t per = (size_t)DISP_RING_MAX_W * DISP_RING_MAX_H;
size_t vram = (size_t)1024 * 512;
@@ -8913,6 +9032,7 @@ static void disp_ring_capture(void)
GpuDisplayInfo di;
gpu_get_display_info(&di);
if (di.disabled || di.width == 0 || di.height == 0) return;
gpu_timeline_note(GTL_DISP_CAPTURE, 0, di.display_x | (di.display_y << 16));
uint32_t w = di.width, h = di.height;
if (w > DISP_RING_MAX_W) w = DISP_RING_MAX_W;
if (h > DISP_RING_MAX_H) h = DISP_RING_MAX_H;
@@ -9350,7 +9470,10 @@ static void handle_dump_buffer(int id, const char *json)
* present path uses (gr_render_wide_display into a scratch buffer), so the dump
* reflects the composited wide frame 1:1, orientation included. Errors if the
* active backend has no wide compositor or native-wide isn't engaged. */
static void handle_wide_shot(int id, const char *json)
/* Write the presented native-wide frame to `path`. Returns NULL on success
* (and the pixel size), or a static error string. Shared by wide_shot and
* the capture mark. */
static const char *write_wide_png(const char *path, int *out_w, int *out_h)
{
extern int gr_wide_supported(void);
extern int gr_scale(void);
@@ -9359,33 +9482,28 @@ static void handle_wide_shot(int id, const char *json)
extern void gl_renderer_sync_cpu(void);
gl_renderer_sync_cpu(); /* no-op on SW / when no GL frame pending */
if (!gr_wide_supported()) { send_err(id, "active backend has no wide compositor"); return; }
if (!gr_wide_supported()) return "active backend has no wide compositor";
int extra = ws_nw_extra();
if (extra <= 0) { send_err(id, "native-wide not engaged (extra=0; need a wide game frame)"); return; }
if (extra <= 0) return "native-wide not engaged (extra=0; need a wide game frame)";
GpuDisplayInfo di;
gpu_get_display_info(&di);
if (di.disabled || di.width == 0 || di.height == 0) { send_err(id, "display disabled"); return; }
if (di.disabled || di.width == 0 || di.height == 0) return "display disabled";
int scale = gr_scale(); if (scale < 1) scale = 1;
int present_w = (int)di.width + extra;
int W = present_w * scale, H = (int)di.height * scale;
char path[512];
if (!json_get_str(json, "path", path, sizeof(path)))
strncpy(path, "psx_wide.png", sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
uint32_t *buf = (uint32_t *)malloc((size_t)W * H * sizeof(uint32_t));
if (!buf) { send_err(id, "alloc failed"); return; }
if (!buf) return "alloc failed";
int n = gr_render_wide_display(buf, W * (int)sizeof(uint32_t),
(int)di.display_x, (int)di.display_y, (int)di.height);
if (n <= 0) { free(buf); send_err(id, "no wide surface for displayed buffer"); return; }
if (n <= 0) { free(buf); return "no wide surface for displayed buffer"; }
/* ARGB8888 (0xAARRGGBB) -> RGB, written in the buffer's row order (so the
* PNG shows exactly the present orientation — the point of this probe). */
uint8_t *rgb = (uint8_t *)malloc((size_t)W * H * 3);
if (!rgb) { free(buf); send_err(id, "alloc failed"); return; }
if (!rgb) { free(buf); return "alloc failed"; }
for (int y = 0; y < H; y++) {
for (int x = 0; x < W; x++) {
uint32_t px = buf[(size_t)y * W + x];
@@ -9397,14 +9515,114 @@ static void handle_wide_shot(int id, const char *json)
}
free(buf);
FILE *f = fopen(path, "wb");
if (!f) { free(rgb); send_err(id, "cannot open file"); return; }
if (!f) { free(rgb); return "cannot open file"; }
int ok = png_write_rgb(f, rgb, (uint32_t)W, (uint32_t)H);
free(rgb); fclose(f);
if (!ok) { send_err(id, "png encode failed"); return; }
if (!ok) return "png encode failed";
if (out_w) *out_w = W;
if (out_h) *out_h = H;
return NULL;
}
static void handle_wide_shot(int id, const char *json)
{
char path[512];
if (!json_get_str(json, "path", path, sizeof(path)))
strncpy(path, "psx_wide.png", sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
int W = 0, H = 0;
const char *err = write_wide_png(path, &W, &H);
if (err) { send_err(id, err); return; }
send_fmt("{\"id\":%d,\"ok\":true,\"path\":\"%s\",\"width\":%d,\"height\":%d}",
id, path, W, H);
}
/* Operator capture mark. Runs on the present thread (hotkey handler), where
* the renderer's context is current. Freezes the display and GP0 rings so the
* frames and draw lists leading up to the mark survive until read, and saves
* the presented native-wide frame (the only record of the compositor output).
* A second mark while frozen is ignored; release with capture_mark op=release. */
void debug_server_capture_mark(void)
{
if (s_capture_frozen) return;
s_capture_mark_frame = s_frame_count;
s_capture_mark_count++;
snprintf(s_capture_wide_path, sizeof s_capture_wide_path,
"capture_mark_%llu_wide.png", (unsigned long long)s_frame_count);
s_capture_wide_ok = write_wide_png(s_capture_wide_path, NULL, NULL) == NULL;
s_capture_frozen = 1;
gpu_gp0_ring_set_frozen(1);
present_image_ring_set_frozen(1);
gpu_timeline_set_frozen(1);
}
/* Presented-image ring (see present_image_ring.h):
* {"cmd":"present_image_ring_stats"} -> oldest, newest, count, frozen
* {"cmd":"present_image_ring_get","frame":N,"path":"x.png"} */
static void handle_present_image_ring_stats(int id, const char *json)
{
(void)json;
uint32_t lo = 0, hi = 0; int n = 0;
present_image_ring_span(&lo, &hi, &n);
send_fmt("{\"id\":%d,\"ok\":true,\"oldest\":%u,\"newest\":%u,\"count\":%d,"
"\"frozen\":%d,\"capacity\":%d,\"height\":%d}",
id, lo, hi, n, present_image_ring_frozen(),
PRESENT_IMAGE_RING_CAP, PRESENT_IMAGE_RING_H);
}
static void handle_present_image_ring_get(int id, const char *json)
{
int f = json_get_int(json, "frame", -1);
char path[512];
if (f < 0 || !json_get_str(json, "path", path, sizeof path)) {
send_err(id, "need frame and path"); return;
}
uint8_t *rgb = NULL; int w = 0, h = 0;
if (!present_image_ring_get_rgb((uint32_t)f, &rgb, &w, &h)) {
send_err(id, "frame not in presented-image ring"); return;
}
FILE *fp = fopen(path, "wb");
int ok = fp && png_write_rgb(fp, rgb, (uint32_t)w, (uint32_t)h);
if (fp) fclose(fp);
free(rgb);
if (!ok) { send_err(id, "png write failed"); return; }
send_fmt("{\"id\":%d,\"ok\":true,\"frame\":%d,\"width\":%d,\"height\":%d}",
id, f, w, h);
}
/* {"cmd":"capture_mark","op":"status"|"mark"|"release"}
* status -> frozen, mark_frame, count, wide_path/wide_ok, and the frozen
* display-ring / GP0-ring frame spans to query.
* mark -> same as the hotkey (use only when nobody is at the window).
* release -> resume continuous capture. */
static void handle_capture_mark(int id, const char *json)
{
char op[16] = "status";
(void)json_get_str(json, "op", op, sizeof op);
if (!strcmp(op, "mark")) debug_server_capture_mark();
else if (!strcmp(op, "release")) {
s_capture_frozen = 0;
gpu_gp0_ring_set_frozen(0);
present_image_ring_set_frozen(0);
gpu_timeline_set_frozen(0);
} else if (strcmp(op, "status")) { send_err(id, "op must be status|mark|release"); return; }
uint32_t oldest = 0, newest = 0;
gpu_gp0_ring_frame_span(&oldest, &newest);
uint32_t d_old = UINT32_MAX, d_new = 0;
for (int i = 0; i < DISP_RING_CAP; i++) {
if (!s_disp_ring[i].valid) continue;
if (s_disp_ring[i].frame < d_old) d_old = s_disp_ring[i].frame;
if (s_disp_ring[i].frame > d_new) d_new = s_disp_ring[i].frame;
}
if (d_old == UINT32_MAX) d_old = 0;
send_fmt("{\"id\":%d,\"ok\":true,\"frozen\":%d,\"mark_frame\":%llu,"
"\"count\":%llu,\"wide_path\":\"%s\",\"wide_ok\":%d,"
"\"display_ring\":[%u,%u],\"gp0_ring\":[%u,%u],\"frame\":%llu}",
id, s_capture_frozen, (unsigned long long)s_capture_mark_frame,
(unsigned long long)s_capture_mark_count, s_capture_wide_path,
s_capture_wide_ok, d_old, d_new, oldest, newest,
(unsigned long long)s_frame_count);
}
/* screenshot: capture what the player is actually being shown. Native-wide
* presentation is wider than the canonical PSX display rectangle, so routing
* this command unconditionally through handle_screenshot_file silently omits
@@ -9687,6 +9905,87 @@ static void handle_present_ring(int id, const char *json)
free(buf);
}
/* Precise-NCLIP attribution (gte_nclip_stats.h).
* {"cmd":"nclip_stats"} totals + per-function + per-site tables
* {"cmd":"nclip_stats","reset":1} clear the tables after reading
* {"cmd":"nclip_stats","exact":0|1} switch exact-site consumers off/on
* funcs: [near_store_pc, nclips, disagree, saturated, last_frame] (1 = unknown)
* sites: [pc, evals, flips, fallbacks, last_frame] */
static void handle_nclip_stats(int id, const char *json)
{
extern int gpu_ws_precise_nclip_enabled(void);
static GteNclipFuncStat funcs[GTE_NCLIP_STAT_CAP];
static GteNclipSiteStat sites[GTE_NCLIP_STAT_CAP];
uint64_t hits = 0, fallbacks = 0, disagree = 0;
gte_nclip_precise_stats(&hits, &fallbacks, &disagree);
const int nf = gte_nclip_func_stats(funcs, GTE_NCLIP_STAT_CAP);
const int ns = gte_nclip_site_stats(sites, GTE_NCLIP_STAT_CAP);
const int bufsz = 256 + (nf + ns) * 72;
char *buf = (char *)malloc((size_t)bufsz);
if (!buf) { send_err(id, "alloc failed"); return; }
int pos = snprintf(buf, bufsz,
"{\"id\":%d,\"ok\":true,\"enabled\":%d,\"exact\":%d,\"hits\":%llu,\"fallbacks\":%llu,"
"\"disagreements\":%llu,\"funcs\":[",
id, gpu_ws_precise_nclip_enabled(), gte_nclip_exact_enabled(),
(unsigned long long)hits,
(unsigned long long)fallbacks, (unsigned long long)disagree);
for (int i = 0; i < nf; i++)
pos += snprintf(buf + pos, bufsz - pos, "%s[\"0x%08X\",%u,%u,%u,%u]",
i ? "," : "", funcs[i].func, funcs[i].nclips,
funcs[i].disagree, funcs[i].saturated, funcs[i].last_frame);
pos += snprintf(buf + pos, bufsz - pos, "],\"sites\":[");
for (int i = 0; i < ns; i++)
pos += snprintf(buf + pos, bufsz - pos, "%s[\"0x%08X\",%u,%u,%u,%u]",
i ? "," : "", sites[i].pc, sites[i].evals,
sites[i].flips, sites[i].fallbacks, sites[i].last_frame);
snprintf(buf + pos, bufsz - pos, "]}");
if (json_get_int(json, "reset", 0)) gte_nclip_stats_reset();
{
const int exact = json_get_int(json, "exact", -1);
if (exact >= 0) gte_nclip_exact_set_enabled(exact);
}
send_fmt("%s", buf);
free(buf);
}
/* GPU frame timeline (gpu_timeline.h): cycle-ordered vblank / flip /
* draw-area / linked-list DMA / present events.
* {"cmd":"gpu_timeline","from":F,"to":T} frames [F,T] (default: last 8)
* events: [seq, cycle, frame, kind, a, b] */
static void handle_gpu_timeline(int id, const char *json)
{
uint64_t total = gpu_timeline_total();
uint64_t oldest = total > GPU_TIMELINE_CAP ? total - GPU_TIMELINE_CAP : 0;
GpuTimelineEntry last;
uint32_t newest_frame = (total && gpu_timeline_get(total - 1, &last)) ? last.frame : 0;
int to = json_get_int(json, "to", (int)newest_frame);
int from = json_get_int(json, "from", to - 7);
if (from < 0) from = 0;
if (to < from) { send_err(id, "to < from"); return; }
int bufsz = 256 + 4096 * 80;
char *buf = (char *)malloc((size_t)bufsz);
if (!buf) { send_err(id, "alloc failed"); return; }
GpuTimelineEntry first_e;
uint32_t oldest_frame = (total && gpu_timeline_get(oldest, &first_e)) ? first_e.frame : 0;
int pos = snprintf(buf, bufsz,
"{\"id\":%d,\"ok\":true,\"total\":%llu,\"frames\":[%u,%u],\"events\":[",
id, (unsigned long long)total, oldest_frame, newest_frame);
int first = 1, n = 0;
for (uint64_t s = oldest; s < total && pos < bufsz - 128 && n < 4096; s++) {
GpuTimelineEntry e;
if (!gpu_timeline_get(s, &e)) continue;
if ((int)e.frame < from || (int)e.frame > to) continue;
pos += snprintf(buf + pos, bufsz - pos, "%s[%llu,%llu,%u,\"%s\",%u,%u]",
first ? "" : ",", (unsigned long long)s,
(unsigned long long)e.cyc, e.frame,
gpu_timeline_kind_name(e.kind), e.a, e.b);
first = 0; n++;
}
pos += snprintf(buf + pos, bufsz - pos, "],\"truncated\":%s}", n >= 4096 ? "true" : "false");
send_fmt("%s", buf);
free(buf);
}
/* ---- Write trace: hook + handlers (Tier 1 reverse debugger) ---- */
extern CPUState *debug_cpu_ptr;
@@ -14194,13 +14493,20 @@ static const CmdEntry s_commands[] = {
{ "gpu_state", handle_gpu_state },
{ "geom_correction", handle_geom_correction },
{ "pgxp", handle_pgxp },
{ "pgxp_shadow", handle_pgxp_shadow },
{ "pgxp_miss_ring", handle_pgxp_miss_ring },
{ "ws_aspect_cone_site", handle_ws_aspect_cone_site },
{ "ws_margin", handle_ws_margin },
{ "ws_hud_mode", handle_ws_hud_mode },
{ "ws_tag_stats", handle_ws_tag_stats },
{ "mod_counters", handle_mod_counters },
{ "capture_mark", handle_capture_mark },
{ "present_image_ring_stats", handle_present_image_ring_stats },
{ "present_image_ring_get", handle_present_image_ring_get },
{ "kernel_bless", handle_kernel_bless },
{ "ws_aspect", handle_ws_aspect },
{ "display_aspect", handle_display_aspect },
{ "window_size", handle_window_size },
{ "ws_nw", handle_ws_nw },
{ "scanline", handle_scanline },
{ "ws_backdrop_ring", handle_ws_backdrop_ring },
@@ -14218,6 +14524,8 @@ static const CmdEntry s_commands[] = {
{ "gl_coh_ring", handle_gl_coh_ring },
{ "gl_present_ring", handle_gl_present_ring },
{ "present_ring", handle_present_ring },
{ "gpu_timeline", handle_gpu_timeline },
{ "nclip_stats", handle_nclip_stats },
{ "frame_perf", handle_frame_perf },
{ "gl_ws_ablate", handle_gl_ws_ablate },
{ "gl_interp", handle_gl_interp },