Skip to content

Linux/wlr capture delivers ~2/3 of the requested frame rate (pacing sleeps before requesting the frame) #5751

Description

@DasunLin

Is there an existing issue for this?

  • I have searched the existing issues

Describe the Bug

On the wlr capture path, the delivered frame rate is capped at roughly two thirds of the
requested rate, regardless of codec, resolution, encoder load or Sunshine version. With a
60 fps client request on a 60 Hz output I get a steady 41 fps; the same host sustains
59.9 fps when an independent wlr-screencopy consumer captures the same output with the
same VA-API encoder.

I believe the cause is the order of operations in the wlr capture loop
(src/platform/linux/wlgrab.cpp, wlr_ram_t::capture / wlr_dmabuf_t::capture):

while (true) {
  platf::handle_pacing(next_frame, delay, sleep_overshoot_logger);  // sleep a full frame interval
  auto status = snapshot(...);   // only now request a frame, then block until the compositor answers
  push_captured_image_cb(...);
}

snapshot() issues the screencopy request after the pacing sleep, so the compositor
cannot answer until its next frame. The delivered interval is therefore quantised to the
output's vblank and, since the request lands at an arbitrary phase, averages about
1.5 vblanks — 25 ms at 60 Hz, which is the 41 fps I measure.

The PipeWire path already has unpaced capture (#5629); wlr does not.

Evidence

Host: Ubuntu 24.04, AMD RX 7900 (VA-API), sway on its headless backend, virtual output
1920x1080. Capture source is glxgears fullscreen, verified window-by-window at 61.8–62.2 fps
for every measurement below. Client is Moonlight 12.2 on Android TV. Rates are read from
Moonlight's performance overlay.

The ceiling does not move for any of these:

varied values tried delivered
codec AV1 / H.264 41 / 41
Sunshine version 2026.516 / 2026.918 41 / 41
thread priority all 46 threads renice −15 41
resolution 1080p / 720p 41 / 41

Independent consumer on the same output, same host, same encoder:

capture encoder frames / duration fps
wf-recorder libx264 796 / 12.98 s 61.3
wf-recorder h264_vaapi 836 / 13.96 s 59.9
Sunshine vaapi — 41

The ceiling tracks the output's refresh, not the requested rate. Holding the client at
90 fps and forcing the output back to 60 Hz collapses it again, which is what points at the
vblank rather than at delay itself:

client request output delivered host processing avg
60 fps 60 Hz 41 15.5 ms
90 fps 60 Hz 42–44 15.1–16.8 ms
90 fps 90 Hz 54–56 12.3–12.8 ms
120 fps 120 Hz 64–73 10.9–12.4 ms

Host processing latency averages 15.5 ms throughout, so the encoder is not the limit — it
would allow ~64 fps on its own.

Expected Behavior

Delivered frame rate should approach the requested rate when the source and the encoder can
both sustain it, as the same protocol does for other consumers.

Additional Context

#5748 is touching snapshot() and the request timing in the same file for a different reason
(idle-screen compositor CPU); whoever works on that area may want this case in view.

Host Operating System

Linux

Operating System Version

Ubuntu 24.04.3 LTS, kernel 7.0.0-31-generic

Architecture

amd64

Sunshine commit or version

2026.918.174827 (also reproduced on 2026.516.143833)

GPU Type

AMD

GPU Model

Radeon RX 7900

GPU Driver/Mesa Version

amdgpu / Mesa (VA-API)

Capture Method

wlr (wlgrab)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions