[BUG]~50ms (6 frames @120fps) display latency regression vs v1.10.0, even with Frame Mixer = None #807

Open
opened 2026-08-28 03:39:20 -05:00 by onlyou2030 · 1 comment
onlyou2030 commented 2026-08-28 03:39:20 -05:00 (Migrated from github.com)

Summary

Current main shows roughly 6 frames (@120fps) ≈ 50ms more display
latency than the v1.10.0 release, on the same machine, network and game.
Bisection points to the frame pacing / presentation rework that landed in
April 2026 (starting around 45237ac9). The regression persists with
Frame Mixer = None (the default), so the cost comes from the scheduling
framework itself, not from frame mixing.

Environment

  • OS: Windows 10 21H2
  • Renderer backend: OpenGL (D3D11VA hw decode)
  • Source: PS5 Remote Play 1920x1080 @ 60fps, LAN, RTT < 1ms
  • Chiaki-ng: self-built main (a875aa08) vs official v1.10.0 build

Measurement method

A 120fps phone camera records the PS5's directly-connected monitor and
the Chiaki client window side by side in the same shot. Latency is counted as
the number of 120fps frames between an action appearing on the PS5's direct
display and the same action appearing in Chiaki. The direct display serves as
a shared zero-reference for both builds, so the delta between builds is
measurement-error independent.

Image Image
  • v1.10.0 (0c4a45df): 7 frames @120fps (~58ms) behind the direct display
  • main (a875aa08, default settings, Frame Mixer = None): 13 frames @120fps
    (~108ms)
    behind the direct display
  • Regression: 6 frames ≈ 50ms

Restoring only gui/src/qmlmainwindow.cpp / qmlmainwindow.h to their
v1.10.0 state on top of main brings latency back to 7 frames,
confirming the regression is fully contained in the presentation-layer rework
in those files (+5348 lines across ~34 commits).

Root cause analysis

v1.10.0 renders arrival-driven: decoded frame → pl_queue → presented at the
next Qt frame. The rework changed this to scheduled presentation:

  1. 45237ac9 — frame timing now follows the screen clock instead of the
    host stream, so frames wait for their scheduled vsync slot
  2. ec3cbecb / 54de4deb — queue depth tolerance increased; pl_queue now
    steadily holds ~2 frames
  3. e6614c67 / c2cc3ce0 — deferred swap thread / thread-based presentation
    adds another pipeline stage
  4. Pending/overflow staging holds frames until their "presentation time" —
    instrumented median holding time is ~42ms on this machine, the same order
    of magnitude as the measured regression

This trade-off makes sense for video playback (smoothness, judder-free frame
mixing, A/V sync), but for game streaming the frame is the input feedback —
every scheduled slot is direct input-to-photon latency.

Frame Mixer is NOT the cause

Frame Mixer defaults to None, and the full 13-frame latency is reproducible
in that state. The mixing feature is idle, but the pacing machinery built for
it (staging queue, screen-clock scheduling, deferred swap) stays on the hot
path unconditionally.

Notably, May saw a revert chain around this code (2c5d9160 → 70cbd238 →
6b1ecefa) plus follow-up fixes for its side effects (FIFO swapchain c650d297,
render gating off 03bba538, swapchain depth 1 bf132abc, stuck-screen fix),
suggesting the new scheduling has been fragile. Possibly related: #743.

Suggested fix

When Frame Mixer = None, the pacing framework isn't needed — presentation
could fall back to arrival-driven rendering (v1.10.0 behavior) in that
configuration, or behind a "low latency mode" toggle. Happy to test patches
on this setup.

## Summary Current `main` shows roughly **6 frames (@120fps) ≈ 50ms** more display latency than the v1.10.0 release, on the same machine, network and game. Bisection points to the frame pacing / presentation rework that landed in April 2026 (starting around 45237ac9). The regression persists with **Frame Mixer = None** (the default), so the cost comes from the scheduling framework itself, not from frame mixing. ## Environment - OS: Windows 10 21H2 - Renderer backend: OpenGL (D3D11VA hw decode) - Source: PS5 Remote Play 1920x1080 @ 60fps, LAN, RTT < 1ms - Chiaki-ng: self-built `main` (a875aa08) vs official v1.10.0 build ## Measurement method A **120fps phone camera** records the PS5's directly-connected monitor and the Chiaki client window side by side in the same shot. Latency is counted as the number of 120fps frames between an action appearing on the PS5's direct display and the same action appearing in Chiaki. The direct display serves as a shared zero-reference for both builds, so the delta between builds is measurement-error independent. <img width="1928" height="1081" alt="Image" src="https://github.com/user-attachments/assets/5c518733-7ec3-4394-9819-e05eae58db0f" /> <img width="1921" height="1084" alt="Image" src="https://github.com/user-attachments/assets/913a973f-6a78-45db-bfc6-7ac83cace0dd" /> - v1.10.0 (0c4a45df): **7 frames @120fps (~58ms)** behind the direct display - `main` (a875aa08, default settings, Frame Mixer = None): **13 frames @120fps (~108ms)** behind the direct display - **Regression: 6 frames ≈ 50ms** Restoring *only* `gui/src/qmlmainwindow.cpp` / `qmlmainwindow.h` to their v1.10.0 state on top of `main` brings latency back to **7 frames**, confirming the regression is fully contained in the presentation-layer rework in those files (+5348 lines across ~34 commits). ## Root cause analysis v1.10.0 renders arrival-driven: decoded frame → `pl_queue` → presented at the next Qt frame. The rework changed this to scheduled presentation: 1. **45237ac9** — frame timing now follows the *screen clock* instead of the host stream, so frames wait for their scheduled vsync slot 2. **ec3cbecb / 54de4deb** — queue depth tolerance increased; `pl_queue` now steadily holds ~2 frames 3. **e6614c67 / c2cc3ce0** — deferred swap thread / thread-based presentation adds another pipeline stage 4. Pending/overflow staging holds frames until their "presentation time" — instrumented median holding time is ~42ms on this machine, the same order of magnitude as the measured regression This trade-off makes sense for video playback (smoothness, judder-free frame mixing, A/V sync), but for game streaming the frame *is* the input feedback — every scheduled slot is direct input-to-photon latency. ## Frame Mixer is NOT the cause Frame Mixer defaults to `None`, and the full 13-frame latency is reproducible in that state. The mixing feature is idle, but the pacing machinery built for it (staging queue, screen-clock scheduling, deferred swap) stays on the hot path unconditionally. Notably, May saw a revert chain around this code (2c5d9160 → 70cbd238 → 6b1ecefa) plus follow-up fixes for its side effects (FIFO swapchain c650d297, render gating off 03bba538, swapchain depth 1 bf132abc, stuck-screen fix), suggesting the new scheduling has been fragile. Possibly related: #743. ## Suggested fix When Frame Mixer = None, the pacing framework isn't needed — presentation could fall back to arrival-driven rendering (v1.10.0 behavior) in that configuration, or behind a "low latency mode" toggle. Happy to test patches on this setup.
streetpea commented 2026-09-11 20:07:44 -05:00 (Migrated from github.com)

can you try the direct frame video option in this build: https://github.com/streetpea/chiaki-ng/actions/runs/34158163343/artifacts/10031923419 @onlyou2030

can you try the direct frame video option in this build: https://github.com/streetpea/chiaki-ng/actions/runs/34158163343/artifacts/10031923419 @onlyou2030
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-streetpea/chiaki-ng#807
No description provided.