Presentation & Latency
Present Modes
Game windows (game builds, PIE windows) present with the configured mode; editor and tool windows always use mailbox.
| Mode | Images | Tearing | Notes |
|---|---|---|---|
| Mailbox (default) | 3 | No | Renders ahead of vblank; the newest finished image is shown |
| Immediate | 2 | Yes | "VSync off" — lowest latency; falls back to mailbox, then FIFO |
| Fifo | 2 | No | Strict vsync queue; highest latency, lowest power |
Set with PresentMode = Mailbox | Immediate | Fifo in the project INI, or --present-mode <mode> on the command line.
Measuring Latency
Run any build with --latency (or LatencyStats = 1). Once per second the renderer logs:
[Latency] Mailbox, 3 images, 473 fps | Input->Photon 13.84 avg / 16.54 p95 ms | Visual 8.42 / 8.69 | input->render 5.42, render->present 0.05, present->display 8.37 | 475 samples
- Input→Photon — start of the input window consumed by the displayed sim frame → the frame reaching the display. Conservative: input arriving later in that window waits less.
- Visual — render thread starting the frame → displayed.
- Breakdown — input→render (sim + publish + render pickup), render→present (CPU record/submit), present→display (the swapchain queue; where the present modes differ).
Every main-window present carries a VK_KHR_present_id; the render thread polls completion with a zero-timeout vkWaitForPresentKHR (VK_KHR_present_wait), so measurement never blocks the frame. Without present-wait support the display time is estimated as present call + one refresh, and the log says so. Implementation: LatencyMeter (src/).
Baseline (2026-10, 240 Hz display, windowed 1920×1080, release Playground, uncapped)
| Mode | FPS | Input→Photon avg / p95 | Visual avg / p95 | input→render | present→display |
|---|---|---|---|---|---|
| Fifo | 240 | 18.5 / 24.5 ms | 10.8 / 16.7 | 7.6 | 10.7 (≈2.5 refreshes) |
| Mailbox | ~470 | 13.9 / 17 ms | 8.5 / 8.7 | 5.4 | 8.4 (≈2 refreshes) |
| Immediate | ~1900 | 5.8 / 7.1 ms | 2.4 / 3.2 | 3.4 | 2.4 |
Double-buffered FIFO is not the low-latency option: the render thread fills the queue as soon as an image frees up, so frames wait behind vblank, and it then blocks on acquire and starts the next frame from an already-old sim frame. Mailbox is the default; Immediate is the competitive "VSync off" option.
Caveats: windowed (compositor in the path), not exclusive fullscreen; the moment present_wait reports completion is driver-defined; capped-frame-rate runs not yet measured.
Planned: Present-Timing Frame Pacing
The same present-timing data can drive pacing. Instead of rendering as soon as an image is free, the render thread predicts the next vblank from observed display times and delays its start (and the sim-frame pickup) to just before it, so the frame shown is built from the freshest published sim frame. Goal: tear-free modes (Mailbox, FIFO) within a millisecond or two of Immediate, with lower power than uncapped Mailbox. Tracked in Status & Roadmap.