Skip to content

Conversation

@trin94
Copy link
Member

@trin94 trin94 commented Oct 18, 2025

Last updated at 13:25 Saturday, 15 November 2025 Coordinated Universal Time (UTC)

Replace the direct renderer with a threaded offscreen renderer that
decouples video rendering from UI rendering to improve UI
responsiveness.

Updated Video Rendering:
- Windows: unchanged - mpvQC uses winId/HWND embedding as before
- Linux: threaded offscreen renderer (replaces direct rendering)

Technical Implementation:
The new renderer uses double-buffered framebuffers with rendering in a
dedicated background thread. The UI thread blits the latest completed
frame during its render operations:

  ┌─────┐     ┌────────┐     ┌─────────┐     ┌────────┐     ┌────────┐
  │ mpv │  →  │ render │  →  │ display │  →  │   Qt   │  →  │ screen │
  │     │     │  fbo   │     │   fbo   │     │  fbo   │     │        │
  └─────┘     └────────┘     └─────────┘     └────────┘     └────────┘
                   └─ pointer swap ─┘             │
  └────────── Background thread ──────────┘       │
                                                  │
                                    └── UI thread ┘
                                          blit

This allows the UI to remain responsive at the display's refresh rate
while video frames render independently at the content's native
framerate.

Audio Delay Compensation:
The double-buffered design introduces variable latency as frames wait
in display fbo for the next Qt render cycle. Assuming Qt processes
the render request on the next vsync cycle, frames wait on average
half a display frame period before being blitted to screen.

  ┌─────────────────────────────────────────────────────────────────┐
  │ Consider a 60Hz display (16.67ms frame time):                   │
  │                                                                 │
  │   Display vsync:  |                                 |           │
  │   Time (ms):      0                                16.67        │
  │                                                                 │
  │   Frame arrives at t=5ms:                                       │
  │                   |         F                       |           │
  │                             ←------11.67ms wait-----→           │
  │                                                                 │
  │   Frame arrives at t=12ms:                                      │
  │                   |                       F         |           │
  │                                           ←-4.67ms--→           │
  │                                                                 │
  │   Average wait = 16.67ms / 2 ≈ 8.33ms                           │
  └─────────────────────────────────────────────────────────────────┘

To compensate, mpvQC automatically adjusts mpv's audio delay to half
the current monitor's frame time: (1 / refresh_rate) / 2. This value
updates dynamically when the window moves to a different monitor or
when the refresh rate changes.

  60Hz:  8.33ms
  120Hz: 4.17ms
  144Hz: 3.47ms
  240Hz: 2.08ms
  360Hz: 1.39ms

BREAKING CHANGE: Linux users must update their mpv.conf via Options ->
Edit mpv.conf by either restoring default values or removing the
video-timing-offset option. The new renderer handles timing
automatically through audio delay compensation.

@trin94 trin94 force-pushed the offscreen-rendering branch from 1b24140 to 32cb221 Compare October 19, 2025 19:36
@trin94 trin94 force-pushed the offscreen-rendering branch 2 times, most recently from a94c615 to ddd1ef3 Compare October 28, 2025 15:36
@mpvqc mpvqc deleted a comment from cocogitto-bot bot Oct 28, 2025
@trin94 trin94 force-pushed the offscreen-rendering branch 4 times, most recently from 163eebc to 7808a9f Compare November 2, 2025 11:55
@trin94 trin94 changed the title feat(linux): implement threaded OpenGL rendering with double buffering feat(linux)!: use experimental renderer by default Nov 2, 2025
@trin94 trin94 force-pushed the offscreen-rendering branch 4 times, most recently from d7d863b to c3f1f03 Compare November 8, 2025 15:08
@trin94 trin94 force-pushed the offscreen-rendering branch 2 times, most recently from 195d17c to 3de3b20 Compare November 10, 2025 18:24
@trin94 trin94 changed the title feat(linux)!: use experimental renderer by default feat(linux)!: decouple video rendering from UI thread Nov 10, 2025
@trin94 trin94 force-pushed the offscreen-rendering branch from 3de3b20 to f6990b8 Compare November 10, 2025 18:27
@trin94 trin94 force-pushed the offscreen-rendering branch 3 times, most recently from 1b981ad to 07eeb1c Compare November 15, 2025 13:19
Replace the direct renderer with a threaded offscreen renderer that
decouples video rendering from UI rendering to improve UI
responsiveness.

Updated Video Rendering:
- Windows: unchanged - mpvQC uses winId/HWND embedding as before
- Linux: threaded offscreen renderer (replaces direct rendering)

Technical Implementation:
The new renderer uses double-buffered framebuffers with rendering in a
dedicated background thread. The UI thread blits the latest completed
frame during its render operations:

  ┌─────┐     ┌────────┐     ┌─────────┐     ┌────────┐     ┌────────┐
  │ mpv │  →  │ render │  →  │ display │  →  │   Qt   │  →  │ screen │
  │     │     │  fbo   │     │   fbo   │     │  fbo   │     │        │
  └─────┘     └────────┘     └─────────┘     └────────┘     └────────┘
                   └─ pointer swap ─┘             │
  └────────── Background thread ──────────┘       │
                                                  │
                                    └── UI thread ┘
                                          blit

This allows the UI to remain responsive at the display's refresh rate
while video frames render independently at the content's native
framerate.

Audio Delay Compensation:
The double-buffered design introduces variable latency as frames wait
in display fbo for the next Qt render cycle. Assuming Qt processes
the render request on the next vsync cycle, frames wait on average
half a display frame period before being blitted to screen.

  ┌─────────────────────────────────────────────────────────────────┐
  │ Consider a 60Hz display (16.67ms frame time):                   │
  │                                                                 │
  │   Display vsync:  |                                 |           │
  │   Time (ms):      0                                16.67        │
  │                                                                 │
  │   Frame arrives at t=5ms:                                       │
  │                   |         F                       |           │
  │                             ←------11.67ms wait-----→           │
  │                                                                 │
  │   Frame arrives at t=12ms:                                      │
  │                   |                       F         |           │
  │                                           ←-4.67ms--→           │
  │                                                                 │
  │   Average wait = 16.67ms / 2 ≈ 8.33ms                           │
  └─────────────────────────────────────────────────────────────────┘

To compensate, mpvQC automatically adjusts mpv's audio delay to half
the current monitor's frame time: (1 / refresh_rate) / 2. This value
updates dynamically when the window moves to a different monitor or
when the refresh rate changes.

  60Hz:  8.33ms
  120Hz: 4.17ms
  144Hz: 3.47ms
  240Hz: 2.08ms
  360Hz: 1.39ms

BREAKING CHANGE: Linux users must update their mpv.conf via Options ->
Edit mpv.conf by either restoring default values or removing the
video-timing-offset option. The new renderer handles timing
automatically through audio delay compensation.
@trin94 trin94 force-pushed the offscreen-rendering branch from 07eeb1c to b342b99 Compare November 15, 2025 13:24
@trin94 trin94 merged commit 163323d into main Nov 15, 2025
6 checks passed
@trin94 trin94 deleted the offscreen-rendering branch November 15, 2025 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants