小米电池休眠自动解除打开后盖 小米电池休眠自动解除( 五 )


小米电池休眠自动解除打开后盖  小米电池休眠自动解除

文章插图
可以看到 x0 就是 CA::display_link_will_fire_handler 。继续分析之前找到的私有符号 __UIUpdateCycleSchedulerStart 的相关实现,可以知道这是因为在非 ProMotion 设备上 _UIUpdateCycleEnabled 返回了 NO 导致的 。
小米电池休眠自动解除打开后盖  小米电池休眠自动解除

文章插图
在返回 NO 的情况下 __UIUpdateCycleSchedulerStart*** 不会执行,CA::display_link_will_fire_handler 也就不会被注册 。
_UIUpdateCycleEnabled 所带来的变化继续研究 _UIUpdateCycleEnabled 相关的代码,笔者发现这个的改动并不是仅仅影响 DisplayLink 驱动方式那么简单 。
当 _UIUpdateCycleEnabled 返回 YES 时,UIKit 会在 UIApplicationMain 中执行 _UIUpdateCycleSchedulerStart 。分析该函数,发现 _UIUpdateCycleEnabled 启用时会调用 [CATransaction setDisableRunLoopObserverCommits:YES] 。
小米电池休眠自动解除打开后盖  小米电池休眠自动解除

文章插图
Core Animation 是绝大部分 iOS 应用的渲染引擎,熟悉 iOS 渲染流程的同学想必都知道它的执行也是由 MainRunLoop 驱动,大致为:
MainRunLoop 因为用户操作/Timer/GCD 等被唤醒,派发相应的事件/回调回调中应用修改 Layer Tree,触发 setNeedsLayout 或 setNeedsDisplayMainRunLoop 即将完成本次执行,在即将休眠前向 Observer 派发 BeforeWaiting 事件BeforeWaiting 中触发 Core Animation 注册的 MainRunLoop Observer,触发事务提交 CA::Transaction::commit():自顶向下触发各种 Layout/Display 等逻辑,更新布局/内容Core Animation 将更新后的 Layer Tree 打包发送给 Render Server5.随后 MainRunLoop 进入休眠
6.Render Server 将打包好的 Layer Tree 解码,生成并提交对应的 draw calls
7.GPU 执行渲染指令,渲染出 FrameBuffer,待后续 VSync 信号来临时上屏展示
上图中 +[CATransaction setDisableRunLoopObserverCommits:YES] 这个调用给了笔者提示,让我们验证一下 CA::Transaction::commit() 在 iOS 15 ProMotion 设备上的执行时机,会发现确实不再由 BeforeWaiting 事件驱动了:
小米电池休眠自动解除打开后盖  小米电池休眠自动解除

文章插图
实际上同样的 Source 0 信号同时也驱动了 CADisplayLink 的回调:
小米电池休眠自动解除打开后盖  小米电池休眠自动解除

文章插图
关注这个 Source 0 的回调符号 runloopSourceCallback,会发现这个 Source0 是由 signalChanges 函数驱动:
小米电池休眠自动解除打开后盖  小米电池休眠自动解除

文章插图
而 signalChanges 又是由多个回调所驱动:
小米电池休眠自动解除打开后盖  小米电池休眠自动解除

文章插图
其中:
runloopObserverCallback 为一个 BeforeWaiting 的 MainRunLoop observer 驱动 。runloopTimerCallback 由 mk_timer 驱动,对应的 mach_port 不明,测试发现其回调频率在 1Hz 左右,但也会不断变化,猜测是某种系统计时器 。inputGroupSignaledCallback 由 mk_timer 驱动,对应的 mach_port 正是 VSync 信号 。
小米电池休眠自动解除打开后盖  小米电池休眠自动解除

文章插图
4.requestRegistrySignaledCallback 由 UIScrollView 在即将开始滑动时驱动 。
小米电池休眠自动解除打开后盖  小米电池休眠自动解除

文章插图
通过上面的分析,笔者有理由认为在 iOS 15 上应用的渲染驱动机制出现了比较大的变化 。其中之一便是 DisplayLink 的驱动源的改变 。
结论iOS 15 上 Apple 改变了在 ProMotion 设备的渲染事件循环的驱动方式,CoreAnimation 的事务提交不再由完全由 RunLoop 驱动,而是涉及了多个信号源系统动态帧率选择的机制会综合考虑使用方设置的 API(如 preferredFrameRateRange)和实际展示的内容的变化频率 。具体对 CADisplayLink 而言:内容低速变化时,CADisplayLink 解锁高刷新率仅影响自身的回调频率,系统仍可能选择较低的屏幕刷新率来降低功耗内容中高速变化时,CADisplayLink 解锁高刷新率可以让系统选择更高的刷新频率,甚至实现锁定 120Hz 的刷新关于如何界定低速/中高速,笔者在下文中 CAAnimation 设置动态帧率 部分做了一些试验,可作为参考 。
同时,默认配置的 CADisplayLink 回调频率更高为 60Hz,无法监控更高频率的刷新事件 。
3.ProMotion 设备中,DisplayLink 不再由 VSync 信号直接驱动,而是在新引入的渲染事件循环中执行 。新版本 iOS 系统实现了某种更复杂的机制来尽可能满足使用者设置的偏好频率进行回调,但并不保证它与 VSync 信号的强关联性 。这意味着默认的 CADisplayLink 的回调频率与实际帧率并不匹配,之前基于 CADisplayLink 进行帧率监控的方案在 ProMotion 设备上变得不再可行 。

推荐阅读