Discover Metal for immersive apps
Graphics & Games 进阶 20m

用 Metal 构建沉浸式 App:CompositorServices 详解

Discover Metal for immersive apps

2023年6月5日

在 Apple 官方观看视频

一句话判断

CompositorServices 是 xrOS 上用 Metal 自建渲染引擎的入口——从 SwiftUI 声明场景到 Metal 渲染循环,再到 ARKit 追踪,一条链路打通。

这场 Session 讲了什么

Pau Sastre Miguel 从 Apple 工程团队出发,讲解了如何用 Metal 在 xrOS 上创建完全沉浸的渲染体验:

架构概览。SwiftUI 负责创建 App 和渲染会话,核心引擎可以用 C/C++ 编写。场景类型选择 ImmersiveSpace(而非 Window 或 Volume),内容类型使用 CompositorLayer。

CompositorServices API。这是连接 Metal 渲染和 xrOS 合成器的桥梁。提供两个核心组件:CompositorLayerConfiguration(定义渲染会话的行为和能力)和 LayerRenderer(渲染会话的接口,用于调度和渲染帧)。

注视点渲染(Foveated Rendering)。通过 MTLRasterizationRateMap 实现,在用户注视的区域使用更高采样率,其他区域降低。这个特性在模拟器上不可用,但在真实设备上对性能至关重要。可以在 Xcode Metal Debugger 中预览 foveation 效果。

LayerRenderer Layout。定义每只眼睛的内容如何映射到 Metal 纹理。Compositor 提供纹理索引,你的引擎为每个纹理渲染对应眼睛的内容。

ARKit 集成。配合 ARKit 获取世界追踪和手部追踪数据,让沉浸内容能与真实空间互动。

值得深挖的点

Foveation 的性能收益不是小数。在注视点区域保持高分辨率的同时,外围区域可以用更低的采样率渲染。这意味着同样视觉质量下,你可以节省大量 GPU 算力,或者在同样算力下获得更高的注视点分辨率。

Info.plist 的默认场景设置。如果你的 App 主场景是 ImmersiveSpace,需要在 Info.plist 中设置 UIApplicationPreferredDefaultSceneSessionRoleCPSceneSessionRoleImmersiveSpaceApplication,否则 SwiftUI 默认会创建一个 window 场景。

渲染线程的独立管理。CompositorLayer ready 事件触发后,你创建自定义引擎实例和渲染线程,在独立线程中运行渲染循环。渲染循环的逻辑和传统游戏引擎类似:等待帧、获取帧信息、渲染内容、提交帧。

代码片段

// SwiftUI 端:声明沉浸式场景
@main
struct ImmersiveApp: App {
    var body: some Scene {
        ImmersiveSpace {
            CompositorLayer { configuration, renderer in
                // 配置 LayerRenderer
                configuration.isFoveationEnabled = true
                
                // 创建自定义引擎并启动渲染循环
                let engine = CustomEngine(renderer: renderer)
                engine.start()
            }
        }
    }
}
// C++ 渲染引擎的核心循环
void CustomEngine::renderLoop() {
    while (true) {
        // 等待下一帧
        auto frame = renderer.waitForNextFrame();
        if (!frame) break;
        
        // 获取 foveation map
        auto foveationMap = frame.getRasterizationRateMap();
        
        // 为每只眼睛渲染
        for (int eyeIndex = 0; eyeIndex < 2; eyeIndex++) {
            auto drawable = frame.getDrawable(eyeIndex);
            renderScene(drawable.texture, 
                       drawable.viewMatrix, 
                       drawable.projectionMatrix,
                       foveationMap);
        }
        
        // 提交帧
        frame.endFrame();
    }
}

最佳实践

  • 始终检查 foveation 是否可用(模拟器不支持),然后条件性地启用。
  • 用 Xcode Metal Debugger 检查 foveation 效果,确保注视点区域清晰、外围区域降级合理。
  • 引擎代码可以用 C/C++ 编写,方便复用现有代码库。
  • 渲染循环放在独立线程中,不要阻塞 SwiftUI 主线程。
  • 配合 ARKit 获取世界追踪数据,让内容与真实空间对齐。

还有什么值得关注

  • “Go beyond the window with SwiftUI” 讲了 ImmersiveSpace 的基础用法
  • RecRoom 是用 Unity + CompositorServices + ARKit 的实际案例
  • MTLRasterizationRateMap 是 Metal 中实现 foveation 的核心类型
  • xrOS 的合成器有低 IPC 开销,设计目标就是最小化渲染延迟
WWDC 2023