为了验证更接近现实情况下 , DisplayLink 和 VSync 信号之间的关系,在连续滑动的情况下笔者人为加入了一个 20ms 的微小卡顿进行测试:
上图中可以看到,ProMotion 屏幕很好的处理了这次卡顿 , 由于三缓冲机制的存在,再 Render Loop 渲染 Surface 4 卡顿期间,通过改变 VSync 间隔,系统尝试将缓冲区中的 Surface 283 与 Surface 250 延迟上屏,尽量缩短了用户看到静止画面的时长 。
随后,主线程恢复执行,可以看到 DisplayLink 的回调频率很快恢复至卡顿前的高水平 。而此时 VSync 信号由于前述卡顿减缓机制的存在频率其实有所降低 。此时二者频率并不吻合 。
这和之前播放慢速动画/慢速滑动的情况很相似 , 由于卡顿加上缓冲机制的存在导致短时间内系统将屏幕的刷新频率降低,但在 CPU 侧依然维持了 DisplayLink 的高速回调,满足了使用方对 preferredFrameRateRange 这一 API 的设置 。
为了进一步分析了这种机制的本质 , 笔者接下来会尝试逆向分析 iOS 15 中的系统库相关实现的改动 。
逆向分析DisplayLink 驱动方式的变化在 CADisplayLink 回调方法上设置断点,分别在 iOS 14 和 15 ProMotion 设备上运行,可以得到:
在 iOS 14 上 , CADisplayLink 是通过 Source 1 mach_port 直接接受 VSync 信号驱动的
在 iOS 15 ProMotion 设备上 , CADisplayLink 不再由 VSync 信号驱动,而是由一个 UIKit 内部的 Source0 信号驱动
在 15 中,CADisplayLink 第一次创建并添加至 RunLoop 的时候,会注册一个 Source 1 信号,这和 14 中行为一致 。
其 callout 回调地址对应符号为同样为 display_timer_callback,同样和 14 中的一致 。
这也可以解释为什么 15 上 VSync 信号确实会唤醒一次 RunLoop,只是这次唤醒并不一定触发 DisplayLink 的回调,这就说明 display_timer_callback 行为和 14 相比一定发生了某种变化 。
display_timer_callback逻辑的变化使用 Hopper 分析 display_timer_callback 的实现,发现 15 和 14 的实现并无区别 。使用 LLDB 进行 debug , 逐步分析,观察到后续调用函数为 CA::Display::DisplayLink::callback , 其关键反汇编代码如下图所示:
观察反汇编代码可以发现,如果 CA::display_link_will_fire_handler 这个 block 返回了 NO,则这次 VSync 信号回调不会触发后续的 CA::DisplayLink::dispatch_items 调用 。
实际上在 LLDB 中也验证了这点:
注意上图中的 _CFRunLoopCurrentIsMain 和上图红框代码接近 , 后续的 blraa 指令看起来很明显是调用了一个 block(上面的 ldr x9 [x8, #0x10] 就是把 invoke 指针从 block 结构体中取出的意思) 。tbz 指令中 w0 寄存器为 block 执行的返回值,为 0(即 NO)时跳转至 0x1848dbc08,而 0x1848dbc08 刚好在 dispatch_items 的调用之后,跳过了该调用 。
推荐阅读
- 小米手机怎么查看本机号码显示未知 小米手机怎么查看本机号码
- 小米9网络不好怎么办
- 锂电池电动车怎样充电更耐用 锂电池电动车怎样充电更耐用些
- 小米手机在哪调屏幕亮时间 小米手机怎么调屏幕亮的时间
- bf3电池是vivo什么型号的手机,vivo电池型号b_f3是哪个型号手机
- 雅迪石墨烯电池正确充电方法 雅迪石墨烯电池正确充电方法图片
- 12ah电池能跑多远 60v12ah电池能跑多远
- 小米新旗舰双11降价:首批澎湃OS+4nm旗舰芯+IP68,不到2500拿下
- 捡来的手环怎样恢复出厂设置 小米手环3怎么恢复出厂设置
- 小米note3清除数据 红米note3怎么清除数据