Use foveated streaming to bring immersive content to visionOS
SwiftUI & UI Frameworks 高级 14m

使用 Foveated Streaming 将沉浸式内容融入 visionOS

Use foveated streaming to bring immersive content to visionOS

2026年6月10日

在 Apple 官方观看视频

一句话判断

这场 Session 最值得关注的一件事是:Apple 终于官方下场做“串流”了,FoveatedStreaming 框架让 visionOS 能直接当 PC 的“高级显示器”,把跑不动的重度 OpenXR 应用通过眼动追踪优化带宽后无线串流过来,而且还能和原生 3D 物体无缝缝合。

这场 Session 讲了什么

visionOS 的 M 系列芯片算力虽然强,但想跑 X-Plane 12 或 iRacing 这种 PC 级的重度 OpenXR 仿真应用,依然吃力。以前开发者只能自己凑一套视频推流方案,延迟高、画质糊,还无法和 visionOS 原生系统交互。

现在 Apple 在 visionOS 26.4 推出了 FoveatedStreaming 框架。它底层集成了 NVIDIA CloudXR SDK,PC 端跑 OpenXR 渲染,把画面通过 Wi-Fi 传给头显。最绝的是它利用了 Apple Vision Pro 的眼动追踪,你盯着的地方传高清,余光部分疯狂压缩,硬生生把无线带宽和延迟压到了可用水平。

这对做工业仿真、大型 PC 游戏和建筑设计可视化的团队是巨大利好。你不需要用 Swift 和 RealityKit 重写整个渲染引擎,只要让 PC 端实现 Foveated Streaming Protocol(注视点流式传输协议),visionOS 端写个几十行的“薄客户端”接收画面,就能直接上架 App Store。

值得深挖的点

注视点流式传输(Foveated Streaming)与物理带宽的极限拉扯

为什么 Apple 要亲自做这个协议?因为 4K 120Hz 的双目 3D 视频流,对目前的 Wi-Fi 6E 甚至 Wi-Fi 7 来说都是噩梦,更别提还要保证低于 20ms 的 Motion-to-Photon(动显)延迟。Apple 的解法是把好钢用在刀刃上:利用设备级眼动追踪,只给注视点(Fovea)区域分配高码率,周边视觉区域大幅降低分辨率和码率。

这种设计的 trade-off 很明显。好处是带宽占用断崖式下降,局域网无线串流成为可能;坑在于,如果眼动追踪数据传给 PC 端的网络有抖动,或者 PC 渲染响应慢了半拍,用户转动眼球时就会看到边缘画质“糊”着追过来的现象(Foveated rendering lag)。Apple 在系统底层做了大量预测和补偿算法来掩盖这个问题,但作为开发者,你依然要在 PC 端严格监控 Pose latency(姿态延迟),Xcode 里专门加了 Foveated Streaming instrument 就是干这个的。

打破“黑盒”,原生 UI 与串流画面的空间缝合

传统的 VR 串流(比如 Virtual Desktop 或 Steam Link),头显端只是个纯粹的视频播放器,串流画面和系统 UI 是割裂的。Apple 这次把 FoveatedStreamingSession 直接塞进了 ImmersiveSpace(沉浸式空间)里。

这意味着串流过来的 OpenXR 画面不再是个贴在你脸上的全屏视频,它可以作为空间中的一个 Layer。你可以在串流的 3D 赛车画面上方,悬浮一个 SwiftUI 写的原生仪表盘窗口;或者用 RealityKit 在串流的工厂模型旁边,放置一个原生渲染的 3D 机械臂。这种“混合渲染”架构让 visionOS 端不再是单纯的显示器,而是承担了 UI 交互和轻量级空间锚点(ARKit)的计算。坑点在于深度缓冲(Depth buffer)的同步,如果 PC 端传过来的画面没有正确的深度信息,原生 3D 物体和串流画面在遮挡关系上就会穿帮。

代码片段

1. 极简连接与空间展示

场景:在 visionOS 端初始化会话,并一键将串流画面放入沉浸式空间。

import SwiftUI
import FoveatedStreaming

@main
struct StreamingClientApp: App {
    // 创建全局唯一的串流会话
    private let session = FoveatedStreamingSession()

    var body: some SwiftUI.Scene {
        // 将 session 直接绑定到 ImmersiveSpace
        // 框架会自动处理画面的空间渲染
        ImmersiveSpace(foveatedStreaming: session) {
            // 这里可以叠加原生的 SwiftUI 3D 控件
        }
    }
}

坑:FoveatedStreamingSession 必须在主线程创建,且一个 App 生命周期内通常只需要一个实例,多次实例化会导致底层解码器资源冲突。

2. 混合渲染:串流画面叠加原生 RealityKit 物体

场景:在 PC 串流过来的汽车模型旁边,用 RealityKit 渲染一个原生的 3D 悬浮参数面板。

import SwiftUI
import RealityKit
import FoveatedStreaming

struct MixedRealityView: View {
    let session: FoveatedStreamingSession

    var body: some View {
        // 在沉浸式空间中同时展示串流内容和原生内容
        ImmersiveSpace(foveatedStreaming: session) {
            RealityView { content in
                // 加载原生的 3D 参数面板模型
                if let panel = try? await Entity(named: "ParamPanel") {
                    // 调整位置,使其悬浮在串流汽车模型的上方
                    panel.position = [0, 1.5, -1.0]
                    content.add(panel)
                }
            }
        }
    }
}

坑:原生 RealityKit 物体和串流画面的光照环境默认是不互通的。如果 PC 端是暗光环境,而 visionOS 端通过 ARKit 获取到现实世界是强光,两者的光影会非常违和,需要手动在 visionOS 端给原生物体加 IBL(基于图像的照明)环境光来尽量“骗”过眼睛。

最佳实践

对于已有 PC OpenXR 项目的团队,不要试图在 visionOS 端重写渲染逻辑。直接 clone Apple 在 GitHub 上的 Windows 端 Sample Code,它已经封装好了 Foveated Streaming Protocol 的 TCP 握手和 NVIDIA CloudXR 的对接。把精力放在 visionOS 端的“薄客户端”体验上,比如用 SwiftUI 做一套优雅的配对界面、断线重连提示,以及利用 ARKit 做物理空间的手柄锚点(像 iRacing 那样把虚拟方向盘对齐到物理方向盘上)。

网络环境是最大的玄学。虽然框架支持云端串流,但首发强烈建议只支持局域网(Local Network)Wi-Fi 6 环境。在代码里务必监听 session.state 的变化,当用户摘下头显(进入 Sleep 状态)时,主动通过 Message Channels 通知 PC 端暂停渲染,别让用户摘下眼镜后 PC 显卡还在狂转。

还有什么值得关注

  • Message Channels(消息通道):框架提供了一个双向数据管道,visionOS 端的 SwiftUI 按钮点击事件可以直接通过它发给 PC 端的 OpenXR 应用,不用自己再搭 WebSocket。
  • Xcode Foveated Streaming Instrument:新增了专门的性能分析模板,能直观看到每一帧的 Pose latency(姿态延迟)和带宽占用,排查卡顿不用再靠猜。
  • Progressive Immersion(渐进式沉浸):串流应用完美支持这种沉浸模式,用户可以像看画中画一样,通过一个“洞”来观察 PC 渲染的 3D 场景,同时不丢失对现实环境的感知。
visionOS FoveatedStreaming OpenXR CloudXR RealityKit