Skip to content

Low FPS on Windows due to recv_timeout quantization #564

Description

@xht0201

Environment

  • OS: Windows 11
  • Weylus: 0.11.4
  • Client: iPad

Summary

On Windows the video frame loop runs at roughly half the intended rate (~23 fps instead of 60). Capture and encoding are NOT the bottleneck — the pacing sleep is. recv_timeout on the video command channel is quantized by the default 15.6 ms Windows timer tick, so the idle wait that should be ~6.5 ms pushing the frame period from 16.67 ms to ~27–31 ms.

Per-frame breakdown (measured on Windows 11)

metric before fix after `timeBeginPer
fps 23.1 52.2
period avg 27.1 ms 16.8 ms
wait avg ~16–27 ms 7.3 ms
capture avg 5.4 ms 4.9 ms
encode avg 4.8 ms 5.2 ms

Capture + encode ≈ 10 ms, well inside the 16.67 ms budget. The entire deficit is the wait.

Root cause

The video loop paces frames via receiver.recv_timeout(timeout) (std::sync::mpsc → WaitForSingleObject on Windows). Timed waits round up to the current system timer tick, which is 15.6 ms by default. Measured in isolation on the same machine:

requested timeout actual, before actual, after timeBeginPeriod(1)
6 ms 15.77 ms 6.50 ms
11 ms 15.60 ms 11.55 ms
16 ms 29.58 ms 16.58 ms

So a 6–11 ms pacing sleep lands on one tick (~15.6 ms) or two (~31 ms), roughly halving the frame rate.

Proposed fix

Raise the timer resolution once at startup and keep it for the process lifetime (the standard approach used by games/rendering apps):

// main.rs, at the top of main(), Windows only
#[cfg(target_os = "windows")]
{
    winapi::um::timeapi::timeBeginPeriod(1);
}

Cargo.toml (winapi features):
winapi = { version = "...", features = [ ..., "mmsystem", "timeapi" ] }

Optionally pair with timeEndPeriod(1) on process exit.

Verification

After the change the wait drops to ~7 ms and the while capture/encode times are unchanged —confirming the deficit was purely the quantized sleep.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions