WWDC Quick Look 💓 By SwiftGGTeam
Optimize app power and performance for spatial computing

Optimize app power and performance for spatial computing

观看原视频

Highlight

Apple 在 visionOS 上引入了始终渲染的显示管线,系统以 90Hz 持续渲染每一帧,开发者必须优化渲染、输入、ARKit、音视频和内存使用,否则应用会因错过渲染截止期限而被终止。

核心内容

空间计算的性能挑战

在 iPhone 或 iPad 上,应用不更新时屏幕内容保持不变,GPU 可以休息。在 visionOS 上,用户每次转头、动手、眨眼,画面都要更新。系统始终以 90Hz 渲染每一帧,无论应用是否在更新内容。

这意味着:

  • 渲染开销从”动态内容”扩展到”静态内容”
  • 系统同时运行空间算法处理多个应用
  • 热管理成为性能瓶颈,而非电池续航

00:39

建立性能计划

开发阶段使用 Instruments 和 Xcode Gauges 分析。RealityKit Trace 是 Instruments 的全新模板,专门用于空间计算应用的性能和功耗分析。

关键原则:

  • 必须在真机上分析,模拟器无法复现真实工作负载
  • 测试多种交互场景:播放音视频、FaceTime、Personas
  • 测试多应用同时运行时的资源竞争
  • 使用 MetricKit 和 Xcode Organizer 收集线上数据

03:34

详细内容

渲染管线优化

visionOS 的渲染管线包含三个环节:

  1. 应用层:更新内容,主线程必须在时限内完成
  2. 渲染服务器:持续处理应用更新、用户输入和空间数据
  3. 合成器:以显示刷新率(通常 90Hz)向屏幕输出帧

如果应用渲染耗时过长,渲染服务器会错过最优渲染延迟的截止期限,导致视觉更新延迟一帧,用户感受到卡顿。严重的渲染停滞会导致应用被终止。

06:28

SwiftUI / UIKit 优化

// 使用 @Observable 替代 @StateObject,减少不必要的布局更新
import SwiftUI

@Observable
class TaskStore {
    var tasks: [Task] = []
}

struct TaskListView: View {
    let store: TaskStore
    
    var body: some View {
        List(store.tasks) { task in
            TaskRow(task: task)
        }
    }
}

关键点:

  • @Observable 提供更细粒度的变更追踪,减少无关的布局更新
  • 避免在重叠的 UI 视图上使用半透明效果,半透明会导致 overdraw
  • 减少阴影、模糊、遮罩等视觉特效,这些会触发离屏渲染
  • 考虑减小窗口默认尺寸,窗口占用的像素越多,渲染工作量越大

08:33

RealityKit 3D 渲染优化

import RealityKit
import SwiftUI

struct OptimizedSceneView: View {
    var body: some View {
        RealityView { content in
            // 异步加载复杂资源,避免阻塞主线程
            if let entity = try? await Entity(named: "optimized_scene", in: realityKitContentBundle) {
                content.add(entity)
            }
        }
    }
}

关键点:

  • 使用 Reality Composer Pro 导出优化后的文件,自动获得纹理压缩
  • 合并共享材质的网格部分,减少独立网格数量
  • 使用较小的三角形和顶点数量
  • 对大型或半透明内容使用 Custom material + unlit surface,避免昂贵的光照计算
  • 提前创建实体,通过 isEnabled 或场景层级控制显隐,避免频繁创建销毁

10:58

Metal 沉浸式体验优化

import Metal
import CompositorServices

// 每帧查询最新的 foveation map 和 pose prediction
func renderFrame(layerRenderer: LayerRenderer) {
    guard let frame = layerRenderer.queryNextFrame() else { return }
    
    // 在最后时刻查询输入数据
    let timing = frame.predictTiming()
    let pose = frame.posePrediction(at: timing.presentationTime)
    let foveation = frame.queryFoveationMap()
    
    // 编码 GPU 工作
    encodeRenderWork(pose: pose, foveation: foveation)
    
    // 确保合成器每帧都能收到新画面
    frame.submit()
}

关键点:

  • 使用 CompositorServices 绕过渲染服务器,直接向合成器提交渲染表面
  • 每帧查询新的 foveation map 和 pose prediction
  • 在编码 GPU 工作前的最后一刻查询输入数据
  • 避免长时间帧停滞,否则系统会终止应用

16:56

输入性能

用户通过眼睛、手、语音和硬件输入与应用交互。输入更新在主线程处理,必须在 8 毫秒内完成(90Hz 刷新率下的最优延迟)。

优化建议:

  • 为 RealityKit 内容添加物理碰撞体时,优先使用 static collider
  • 减少重叠的交互内容,降低命中测试工作量

18:57

ARKit 优化

import RealityKit

// 使用 "once" 追踪模式避免持续追踪开销
let anchor = AnchorEntity(.head, trackingMode: .once)
entity.components.set(AnchorComponent(.head, trackingMode: .once))

关键点:

  • 使用 AnchorComponent"once" 追踪模式,避免持续追踪成本
  • 最小化持久锚点和临时锚点的总数
  • 在需要使用前最后一刻查询 ARKit 数据
  • 不需要场景理解网格的碰撞数据时,及时关闭

20:17

音视频优化

空间音频默认开启,系统实时处理用户位置、环境和音源信息。减少空间音频工作量的三个方向:

  • 减少同时播放的音源数量
  • 减少移动音源数量
  • 缩小 soundstage 尺寸

视频播放建议:

  • 使用 24 或 30fps 视频获得最佳性能和功耗
  • 减少同时播放和渲染的视频数量
  • 视频播放期间最小化 UI 或 3D 内容更新

22:16

热管理与内存压力

import Foundation

// 监听热状态变化,动态调整内容
NotificationCenter.default.addObserver(
    forName: ProcessInfo.thermalStateDidChangeNotification,
    object: nil,
    queue: .main
) { _ in
    let state = ProcessInfo.processInfo.thermalState
    switch state {
    case .serious, .critical:
        reduceVisualQuality()
        lowerUpdateRate()
    default:
        break
    }
}

关键点:

  • 订阅 thermalStateDidChangeNotification,根据热压力动态调整内容
  • 使用 Xcode 的 thermal inducers 模拟高热状态
  • 减少离屏渲染、窗口数量和媒体内容以降低渲染内存分配
  • 降低纹理分辨率、网格几何尺寸和粒子数量以减少 RealityKit 内存

25:19

核心启发

  • 做什么:为 visionOS 应用建立性能基准测试套件

    • 为什么值得做:空间计算平台始终渲染的特性意味着性能回归会直接影响用户体验,甚至导致应用被终止
    • 怎么开始:使用 RealityKit Trace 模板在 Instruments 中记录关键场景的帧时间、GPU 使用率和系统功耗,将数据纳入 CI 流程
  • 做什么:实现动态质量降级系统

    • 为什么值得做:热压力会随时发生,主动降级比被系统终止更好
    • 怎么开始:监听 thermalStateDidChangeNotification,在 .serious 状态时降低材质复杂度、减少粒子数量、降低渲染分辨率
  • 做什么:优化 3D 资产的加载策略

    • 为什么值得做:复杂资产会阻塞主线程并增加内存压力
    • 怎么开始:使用 Entity(named:in:) 异步加载 Reality Composer Pro 导出的优化文件,共享相同资产的实体实例
  • 做什么:为沉浸式体验实现 Metal 渲染管线

    • 为什么值得做:CompositorServices 可以绕过渲染服务器,直接控制帧提交时机
    • 怎么开始:使用 LayerRenderer 查询帧时机、pose prediction 和 foveation map,确保每帧都向合成器提交新画面
  • 做什么:设计多应用共存友好的资源使用策略

    • 为什么值得做:visionOS 用户会同时使用多个应用,资源竞争是常态
    • 怎么开始:在测试时主动运行其他应用,监控共享空间下的渲染延迟和系统功耗,确保应用在资源受限时仍能维持 90Hz

关联 Session

评论

GitHub Issues · utterances