Skip to content

Address review feedback and improve native renderer quality - #497

Merged
fduncanh merged 1 commit into
FDH2:native-macos-rendererfrom
Acelogic:native-macos-renderer
Feb 6, 2026
Merged

fduncanh merged 1 commit into
FDH2:native-macos-rendererfrom
Acelogic:native-macos-renderer

Conversation

@Acelogic

@Acelogic Acelogic commented Feb 6, 2026 •

Copy link
Copy Markdown

Summary

Addresses the three issues raised by @fduncanh in PR #490, plus several rendering quality improvements:

Bug fixes from review:

  • Space bar pause/resume broken - Moved pause check from pre-decode (video_renderer_render_buffer) to post-decode (decompress_callback), so the H.264/H.265 decoder stays fed with reference frames while paused and video resumes instantly on unpause
  • Idle screen hint text - Updated to "Press F to toggle into/out of fullscreen | Space to pause/resume | Q to quit"
  • Cover art disappearing in audio-only mode (-ca) - Preserved cover art state across stream resets in video_renderer_stop(), so album art persists until track changes. Implemented video_renderer_cycle() to properly clear expired cover art

Rendering fixes:

  • Video not displaying on iPad Pro - Added missing kCVPixelBufferIOSurfacePropertiesKey to decoder output buffer attributes, required for CVMetalTextureCacheCreateTextureFromImage to map pixel buffers to Metal textures
  • Error logging - Added logging for Metal texture creation failures (previously silent)

Performance improvements:

  • Triple-buffered GPU pipeline - dispatch_semaphore_create(3) with addCompletedHandler for smooth GPU pipelining
  • Frame pacing - presentDrawable:afterMinimumDuration: with estimated source frame interval to eliminate micro stutters from decode timing jitter
  • Native refresh rate - Uses NSScreen.mainScreen.maximumFramesPerSecond instead of hardcoded 60fps
  • Skip redundant renders - Tracks new-frame flag so GPU work is only done when a new decoded frame is available
  • Stall detection - Returns to idle screen after 2 seconds of no new frames (e.g. when phone is locked)

New feature:

  • FPS debug overlay - Toggle with D key or Debug > Show FPS Overlay menu. Shows render fps, source fps, frame interval, and total frame count

Test plan

  • Screen mirroring from iPhone (H.264)
  • Screen mirroring from iPad Pro (H.264/H.265)
  • Space bar pause and resume during mirroring
  • Audio-only mode with -ca flag - cover art should persist
  • Lock phone during mirroring - should return to idle screen
  • Unlock phone - should resume video
  • FPS overlay toggle with D key
  • Fullscreen toggle with F key
  • Multiple reconnection cycles

🤖 Generated with Claude Code

Fixes the three issues raised in PR FDH2#490 review:
- Space bar pause/resume: moved pause check from pre-decode to post-decode
  so the decoder stays fed and video resumes instantly
- Idle screen: updated hint text to mention fullscreen toggle and pause
- Cover art in audio-only mode (-ca): preserved cover art state across
  stream resets so album art persists until track changes

Additional improvements:
- Fix video not displaying on iPad Pro (missing IOSurface backing on
  decoder output buffers, required for CVMetalTextureCache)
- Add error logging for Metal texture creation failures
- Detect stalled streams (e.g. phone locked) and return to idle screen
- Triple-buffered GPU pipeline with dispatch semaphore for smooth pacing
- Frame pacing via presentDrawable:afterMinimumDuration: to eliminate
  micro stutters from decode timing jitter
- Match display native refresh rate instead of hardcoded 60fps
- Skip redundant GPU work when no new frame has been decoded
- Add toggleable FPS debug overlay (D key or Debug menu)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fduncanh
fduncanh merged commit db6a816 into FDH2:native-macos-renderer Feb 6, 2026
@fduncanh

fduncanh commented Feb 7, 2026

Copy link
Copy Markdown
Collaborator

@Acelogic
In audio-with-coverart mode, I'm still seeing the "Waiting for Airplay screen" appearing and masking the coverart, shortly after it displays....

The pause now works, for the video (in the sense of freezing the screen, and restarting at the current video when resumed),

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants