用 Metal 构建沉浸式 App:CompositorServices 详解
Discover Metal for immersive apps
2023年6月5日
一句话判断
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 中设置 UIApplicationPreferredDefaultSceneSessionRole 为 CPSceneSessionRoleImmersiveSpaceApplication,否则 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 开销,设计目标就是最小化渲染延迟