30-WWDC2018: Metal游戏性能优化

722 阅读5分钟

ARKit系列文章目录

2018年的 Session 612 - Metal game performance optimization (原发表于《WWDC18 内参》 主要内容速览:

  • Game Performance Template:新的游戏性能分析工具,全方面分析性能指标.
  • Frame Pacing(帧率平顺性):避免总是出现一帧快,一帧慢,造成视觉不流畅.
  • Thread Priorities(线程优先级):避免系统级功能在后台运行,影响游戏性能.
  • Thermal States(热状态):小心功耗过高,造成手机高温,CPU降频引起卡顿.
  • Dependency Viewer(依赖关系查看器):帮你找出不必要的任务.

Profiling Tools(分析工具)

  • Instruments:包括Game Performance Template
  • Xcode Metal Frame Debugger:包括下文讲到的Dependency Viewer

Game Performance Template其实是以前三个工具的整合,并添加了新的线程查看视图 打开后主要界面如下:

其中,黄色方块表示活跃的线程数比CPU物理核心数多,可能需要优化: 下图的线程图中,黄色表示被抢占(preempted),灰色表示被阻塞(blocked)

Frame Pacing(帧率平顺性)

Micro Stuttering(微型口吃)

<抱歉,实在找不到更合适的翻译了,WWDC官方中文也是这么翻译的>

就是帧率的不协调:

  • 帧生成时间大于显示器刷新间隔
  • 游戏逻辑时间错误

比如下面两个游戏画面,左边的试图以60帧运行,但实际只能达到40帧;右边的则持续稳定在30帧运行: 上图左边帧率高,反而看起来有一卡一顿的现象,这就是Micro Stuttering(微型口吃)导致的,其原理如下图: 这是个常见的三重缓冲,从左侧看,C缓冲在CPU上处理,B缓冲在GPU上处理,而A缓冲用于显示.

  1. 0--1时间内,由于B缓冲在GPU上还没有处理好,不能交给Display显示,所以A缓冲只好显示了两帧时间0--2.
  2. 然后Display开始显示B缓冲内容,并将A交给CPU,但C缓冲在3之前准备好了,所以B缓冲只显示了一帧时间2--3.
  3. 接着3--4时间内,由于A在GPU上处理时间过长,没有在一帧内完成,所以C缓冲又显示了两帧3--5.
  4. 又一个循环,同第1步类似.

我们一般的代码就是,尽快向GPU提交生成的图形,这样的代码就可能造成Micro Stuttering(微型口吃):

// Render Scene
//...
// Get drawable and present
if let drawable = view.currentDrawable {
 // Render Final Pass
 //...
 commandBuffer.present(drawable)
}
commandBuffer.commit()

注意:不要使用usleep()来让各个帧同步!!!!!

实际开发中的Micro Stuttering

实际开发中,Game Performance Template可以标识出丢帧现象,只要按住Option拖动,就可看到详情: 这里显示的,和我们前面分析的一模一样:

最佳实践

应该明确指定帧率. 可使用的API(iOS 10.3+)有:

  • MTLDrawable addPresentedHandler
  • MTLCommandBuffer presentDrawable afterMinimumDuration
  • MTLCommandBuffer presentDrawable atTime
// Render Scene
//...
// Get drawable and present at 30 FPS
if let drawable = view.currentDrawable {
 // Render Final Pass
//...
 let duration = 33.0 / 1000.0 // Duration of 33 ms
 commandBuffer.present(drawable, afterMinimumDuration: duration)
}
commandBuffer.commit()

Thread Priorities(线程优先级)

这是一种诡异的卡顿情况,一般是由于有系统任务在后台运行导致的,比如查收电子邮件,iCloud同步等

Thread Stalling(线程停滞)

渲染线程可能因为优先级较低,而被其他线程抢占:

  • Priority decay(优先级衰减)
  • Priority inversion(优先级反转)

下图灰色表示某些后台任务

实际开发中的Thread Stalling

实际开发中,Game Performance Template工具可以看到该现象: 你会发现GPU空闲,app的CPU各线程也空闲,但物理CPU却忙个不停,造成了卡顿.

点击左侧可以切换显示状态,发现一个线程被抢占了,选中后发现,该线程优先级只有26,于是被系统后台任务itunesstore任务给抢占了CPU时间:

最佳实践

配置渲染线程:

  • Priority 45
  • 退出监测服务Quality of Service

代码如下:

...
r = pthread_attr_init(&attr);
r = pthread_attr_setschedpolicy(&attr, SCHED_RR); // Opt out of Quality of Service 
struct sched_param param = {.sched_priority = 45}; // Configure priority 45
r = pthread_attr_setschedparam(&attr, &param); // Set priority
r = pthread_create(&posixThreadID, &attr, &PosixThreadMainRoutine, NULL); r = pthread_attr_destroy(&attr);
...

Thermal States(发热状态)

要从设计开始,就考虑到性能的持续性,不要一开始疯狂高负载,几分钟后降频卡顿.

Thermal Throttling(热节流)

以下都会影响系统的性能:

  • 设备温度过高
  • 启用了低电量模式

最佳实践

可使用下面的API:

  • (iOS11.0+) NSProcessInfo thermalState
  • (iOS 9.0+) NSProcessInfo lowPowerModeEnabled
  • (iOS 10.3+) MTLCommandBuffer GPUStartTime/GPUEndTime 代码如下:
// Determine thermal state
switch ProcessInfo.processInfo.thermalState {
case .fair:
// Thermals are fair
// Consider taking proactive measures to prevent higher thermals
case .serious:
// Thermals are highly elevated
// Help the system by taking corrective action
case .critical:
// Thermals are extremely elevated
// Help the system by taking immediate corrective action
default:
// Thermals are okay
// Go about your business
// 正常情况
}

过热时调整负载

  • 锁定到一个可持续的帧率
  • 降低分辨率
  • 简化阴影贴图
  • 使用更小的纹理
  • 降低几何体的细节级别(LOD:level of detail)
  • 简化后置处理(post-processing)和特效.

Unnecessary GPU Work(不必要的GPU任务)

被浪费的GPU时间

  • 大尺寸的资源
  • 无用的GPU工作

最佳实践

对GPU进行性能分析:

  • 理解每个渲染特性的花费
  • 移除过度的任务

Metal System Trace

可以用Metal System Trace来查看时间消耗:

但是很多时候,你只看到时间很长,却不知道为什么时间长,于是今年苹果引入了别一个工具:Dependency Viewer

Dependency Viewer

它能让你以图表形式查看各个渲染通道里面,到底做了什么事情,而且是每个项目都带有详细的标签来说明:

Demo,找到隐藏的复杂度

你以为一个任务是这样,只有4个pass: 实际上,用Dependency Viewer可以看到有很多隐藏的渲染任务:

总结

  • 尽可能早的,经常性的进行性能分析.
  • 锁定一个可持续的帧率.
  • 设置正确的线程优先级.
  • 适应系统负载和热状态.
  • 不要向GPU提交无用的任务

WWDC2018相关视频:

WWDC往年相关视频: