Skip to content

feat: IOSurface keepalive for static screens#1

Closed
lukemarsden wants to merge 1 commit into
utm-edition-venus-helixfrom
feature/frame-keepalive
Closed

feat: IOSurface keepalive for static screens#1
lukemarsden wants to merge 1 commit into
utm-edition-venus-helixfrom
feature/frame-keepalive

Conversation

@lukemarsden

Copy link
Copy Markdown
Contributor

Summary

  • Adds a keepalive thread that re-encodes the last blitted IOSurface to VideoToolbox when no page flips arrive for 500ms
  • Produces valid H.264 frames with proper frame_num sequencing and correct reference lists
  • Replaces the previous approach in desktop-bridge which re-sent already-encoded P-frames, corrupting the decoder's DPB (Decoded Picture Buffer)

How it works

When the guest screen is static, virtio_gpu_cmd_resource_flush stops firing and the encoder goes idle. The keepalive thread:

  1. Checks every 250ms if any scanout has been idle for >500ms
  2. If idle and clients are subscribed, wraps the last IOSurface[last_blit_slot] as a new CVPixelBuffer (zero-copy)
  3. Submits to VTCompressionSessionEncodeFrame with a new PTS (continuing the monotonic sequence)
  4. VideoToolbox produces a proper P-frame that references the previous frame correctly

No GL blit is needed — the IOSurface still holds the last raw pixel data. The keepalive respects all existing safety mechanisms (vt_busy, blit_slot_busy, fe->mutex).

Files changed

  • hw/display/helix/helix-frame-export.h — Added keepalive fields to HelixScanoutEncoder, HELIX_KEEPALIVE_INTERVAL_MS constant
  • hw/display/helix/helix-frame-export.m — Keepalive thread, tracking in helix_scanout_frame_ready(), init/reset

Test plan

  • Build QEMU with changes
  • Start a VM and connect a streaming client
  • Verify frames continue at ~2 FPS when the screen is static
  • Verify no H.264 decoder corruption (clean P-frames, no visual artifacts)
  • Verify keepalive stops when no clients are connected
  • Verify normal 60 FPS encoding still works during active rendering

🤖 Generated with Claude Code

When the guest screen is static (no resource_flush events), the encoder
previously went idle and produced no frames. Connected clients would see
a frozen stream until the next page flip.

This adds a keepalive thread that periodically re-submits the last
blitted IOSurface to VideoToolbox when no page flips arrive for 500ms.
This produces valid H.264 frames with proper frame_num sequencing and
correct reference lists — unlike the previous approach in desktop-bridge
which re-sent already-encoded P-frames and corrupted the decoder's DPB.

The keepalive is efficient:
- No GL blit needed — the IOSurface still holds the last raw frame
- Zero-copy CVPixelBuffer wrapping (same as normal path)
- Only fires when clients are subscribed
- Respects VT busy flags to avoid main loop stalls
- ~2 FPS on idle screens (500ms interval)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Thank you for your interest in the QEMU project.

This repository is a read-only mirror of the project's repostories hosted
on https://gitlab.com/qemu-project/qemu.git.
The project does not process merge requests filed on GitHub.

QEMU welcomes contributions of code (either fixing bugs or adding new
functionality). However, we get a lot of patches, and so we have some
guidelines about contributing on the project website:
https://www.qemu.org/contribute/

@github-actions github-actions Bot closed this Feb 15, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Feb 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant