Skip to content

Video Super Resolution for Moonlight iOS/tvOS - #704

Open
linckosz wants to merge 1 commit into
moonlight-stream:masterfrom
linckosz:master
Open

Video Super Resolution for Moonlight iOS/tvOS#704
linckosz wants to merge 1 commit into
moonlight-stream:masterfrom
linckosz:master

Conversation

@linckosz

@linckosz linckosz commented Apr 7, 2026

Copy link
Copy Markdown

Video Super Resolution (VSR) brings to video what DLSS/FSR bring to 3D rendering.
This PR introduces high-quality spatial upscaling on iOS/tvOS using Metal, Core Image, and MetalFX SpatialScaler.

Mobile devices and Apple TVs can benefit from receiving a lower base stream resolution while reconstructing a sharper final image locally on-device.
This helps reduce bandwidth pressure and decoder workload while preserving visual quality at display resolution.

The Apple ecosystem requires an upscaling solution that is:

  • lightweight
  • GPU-driven
  • compatible with the native video pipeline
  • able to preserve HDR/color metadata correctly

MetalFX SpatialScaler is a strong fit for these constraints.

It provides a hardware-accelerated spatial upscaling path designed for Apple GPUs, with good quality and limited CPU overhead.
In this implementation, the decoded frame is first converted from YUV to RGB on the GPU, then optionally upscaled to the display resolution before being submitted to the existing display pipeline.

The observed power impact was very small, around +20%.
On an iPhone 13 Pro, during a 720p60 streaming session, battery usage was about 15% without VSR and about 18% with VSR enabled.

Result

400% Zoom:
Result

Original:
Original

Video Super Resolution:
VideoSuperResolution

Existing VSR Implementations

Video Super Resolution is also available in my other Moonlight ports:

This commit adds an optional Video Super Resolution path for iOS/tvOS.

- Introduce a Video​Super​Resolution renderer backed by Metal and Core Image.
- Convert decoded YUV frames to RGB and upscale them with MetalFX SpatialScaler.
- Preserve the existing rendering path as the default fallback.
- Add a user-facing toggle and hide it when MetalFX.
- Show "Video ​Enhancement: ​Metal​FX " in the performance overlay when active.
- Run the VSR path on a dedicated queue and cache session resources for better latency.
- Preserve HDR/color attachments through the converted output frames.
bartleman added a commit to bartleman/moonlight-ios that referenced this pull request Aug 17, 2026
…am#704

Adds an optional MetalFX spatial upscaler to the tvOS video pipeline,
ported from upstream PR moonlight-stream#704 with tvOS-specific fixes and hardening.
Off by default; when enabled it decodes on a dedicated queue, converts
YUV->RGB via Core Image, and MetalFX-upscales the stream to the display
size before enqueuing to the display layer. Only engages when the display
is larger than the stream (e.g. 1080p -> 4K); at native 4K it is bypassed.

tvOS integration the PR omitted / got wrong:
- Add the "Video Super Resolution (MetalFX)" toggle to Moonlight TV
  Settings.bundle (key videoSuperResolution, default off). Without this the
  feature was permanently off on Apple TV as shipped.
- Skip the PR's iOS-only SettingsViewController + storyboard changes.

Hardening over the raw PR:
- +[VideoSuperResolution isDeviceSupported] gates the feature on actual
  MetalFX MTLFXSpatialScaler support so unsupported GPUs fall back to the
  normal pipeline instead of dropping every frame (black screen).
- Disable VSR for the session when the display is not larger than the
  stream, or when scaler configuration fails, instead of running a pointless
  second decode/convert or dropping frames.
- Runtime per-frame fallback: after repeated failures, revert to direct
  enqueue and request a fresh IDR (avoids mixing decoded-RGB and mid-GOP
  compressed samples on one layer).
- Wait for GPU completion before handing the upscaled buffer to the display
  layer (runs on the VSR queue, not main, so no input-latency cost) to fix a
  tearing / pixel-buffer-pool recycle race.
- Make the HDR reconfigure path serialize on the VSR queue without a
  queue->main->queue deadlock against stop().

MetalFX auto weak-links (introduced tvOS 16 > deployment target), so no
extra linker flags are needed. Private-API-free; fork/self-signed only is
not a concern here (this is standard framework usage).

Claude-Session: https://claude.ai/code/session_01QiVtyqxowFKZc8gKnNjevy
bartleman added a commit to bartleman/moonlight-ios that referenced this pull request Aug 17, 2026
…am#704

Adds an optional MetalFX spatial upscaler to the tvOS video pipeline,
ported from upstream PR moonlight-stream#704 with tvOS-specific fixes and hardening.
Off by default; when enabled it decodes on a dedicated queue, converts
YUV->RGB via Core Image, and MetalFX-upscales the stream to the display
size before enqueuing to the display layer. Only engages when the display
is larger than the stream (e.g. 1080p -> 4K); at native 4K it is bypassed.

tvOS integration the PR omitted / got wrong:
- Add the "Video Super Resolution (MetalFX)" toggle to Moonlight TV
  Settings.bundle (key videoSuperResolution, default off). Without this the
  feature was permanently off on Apple TV as shipped.
- Skip the PR's iOS-only SettingsViewController + storyboard changes.

Hardening over the raw PR:
- +[VideoSuperResolution isDeviceSupported] gates the feature on actual
  MetalFX MTLFXSpatialScaler support so unsupported GPUs fall back to the
  normal pipeline instead of dropping every frame (black screen).
- Disable VSR for the session when the display is not larger than the
  stream, or when scaler configuration fails, instead of running a pointless
  second decode/convert or dropping frames.
- Runtime per-frame fallback: after repeated failures, revert to direct
  enqueue and request a fresh IDR (avoids mixing decoded-RGB and mid-GOP
  compressed samples on one layer).
- Wait for GPU completion before handing the upscaled buffer to the display
  layer (runs on the VSR queue, not main, so no input-latency cost) to fix a
  tearing / pixel-buffer-pool recycle race.
- Make the HDR reconfigure path serialize on the VSR queue without a
  queue->main->queue deadlock against stop().

MetalFX auto weak-links (introduced tvOS 16 > deployment target), so no
extra linker flags are needed. Private-API-free; fork/self-signed only is
not a concern here (this is standard framework usage).
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.

1 participant