30-WWDC2018: Metal游戏性能优化

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时间:

top Created with Sketch.