小米电池休眠自动解除 手机充不进电7个小妙招( 四 )


减少卡顿造成的显示延迟在主线程发生卡顿导致滑动中某一帧渲染耗时过长时,系统会改变这一帧所对应的 VSync 信号间隔(下图 Surface 5),减小从渲染到展示的延时,从而减缓用户感知到的卡顿时长 。

DisplayLink 不完全跟随 VSync 信号如图是一张滑动中场景的 CADisplayLink 回调 和 Display/VSync 事件对照记录 。和之前不同的是,再 ProMotion 设备上 DisplayLink 和 VSync 信号之间没有表现出明显的跟随关系:

具体而言:
第三个箭头所指向的 DisplayLink 的回调并不及时 。在这之前主线程的卡顿已经结束,并且额外执行了两次 RunLoop,但直到第三次才调用了 DisplayLink 的回调 。不仅仅是时机不匹配,也存在收到 VSync 但不触发 DisplayLink 回调的情况(并且主线程处于空闲状态),例如上图中的 ? 处 。解除 DisplayLink 的帧数限制我们知道 , 在 iOS 15 上 Apple 对第三方应用的显示帧率默认做了限制 。第三方应用需要在 Info.plist 中添加<key>CADisableMinimumFrameDurationOnPhone</key><true/> 字段才可以解锁 120Hz 的刷新率 。
于此同时 , 在 iOS 15 中,CADisplayLink 等动画相关 API 也新增了一个用于配置偏好帧率的属性:
/* Defines the range of desired callback rate in frames-per-second for thisdisplay link. If the range contains the same minimum and maximum frame rate,this property is identical as preferredFramesPerSecond. Otherwise, the actualcallback rate will be dynamically adjusted to better align with otheranimation sources. */@property(nonatomic) CAFrameRateRange preferredFrameRateRangeAPI_AVAILABLE(ios(15.0), watchos(8.0), tvos(15.0));为了进一步探究新设备上 DisplayLink 和 VSync 信号之间的关系 , 笔者将测试 App 的 Core Animation 的帧率限制解除,并配置对应的 API,分别在不同的场景重新进行测试:
显示动态内容的场景动画场景展示一个速度中等的位移动画,得到下图:

可以很直观地发现,DisplayLink 解锁帧率后的屏幕刷新率基本稳定在 120Hz 。并且 VSync 和 DisplayLink 的关系似乎又重新一一对应了起来 。
但是,将动画速度减慢,笔者发现这种对应关系发生了变化:

可以观察到在播放慢速动画时,DisplayLink 的频率依然是配置的 120Hz,但是实际的屏幕刷新率却只有 30Hz 。
滑动场景让我们换一种场景再次进行测试,快速滑动视图,在 Instruments 中得到下图:

可以发现,DisplayLink 解锁帧率后,屏幕刷新率同样基本稳定在 120Hz , 仅在丢帧时有降频 。
需要注意的是笔者在 CADisplayLink 的回调中除了调用 os_signpost 上报 log 外无任何 UI 改动 。即便笔者展示的 TableView 极其简单,上图中仍然可以观察到丢帧 , 无法在滑动中完美稳定 120Hz 。这也许说明 UIKit 的渲染性能在 120Hz 下会有某种程度上的原生瓶颈 。然后降低滑动屏幕的速度,得到了和慢速动画相似的结果,尽管 DisplayLink 回调速度不减 , 但是 VSync 信号频率一直保持在较低的水平:

卡顿场景上面两次测试都接近理想情况,即整个 Render Loop 执行几乎没有延迟与卡顿 。但是现实中应用的运行总是有着各种各样的或大或小的卡顿问题 。

推荐阅读