Presentation & Latency

← Dirty Bit Upload | Home


Present Modes

Game windows (game builds, PIE windows) present with the configured mode; editor and tool windows always use mailbox.

ModeImagesTearingNotes
Mailbox (default)3NoRenders ahead of vblank; the newest finished image is shown
Immediate2Yes"VSync off" — lowest latency; falls back to mailbox, then FIFO
Fifo2NoStrict 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/Runtime/Rendering/Public/LatencyMeter.h).

Baseline (2026-10, 240 Hz display, windowed 1920×1080, release Playground, uncapped)

ModeFPSInput→Photon avg / p95Visual avg / p95input→renderpresent→display
Fifo24018.5 / 24.5 ms10.8 / 16.77.610.7 (≈2.5 refreshes)
Mailbox~47013.9 / 17 ms8.5 / 8.75.48.4 (≈2 refreshes)
Immediate~19005.8 / 7.1 ms2.4 / 3.23.42.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.