iOS性能监控(iOS 高刷屏监控 + 优化:从理论到实践全面解析)

背景今年苹果推出了支持推广屏的iPhone设备,使得iPhone 13元Pro和iPhone 13 Pro Max上的App最高刷新率达到120Hz元Hz,大大优化了应用滑动/动画的流畅体验。推广并不是一个新概念。早在2017年,苹果推出的第二代iPad Pro就搭载了这块刷新率高达120Hz元Hz的屏幕。在iPad上,所有应用程序都默认启用高刷新率。或许是因为能耗的原因,在iPhone上,苹果并没有自动为所有app启用该功能,而是需要开发者手动添加配置项进行适配。
最近有报道称iOS 15.4 beta已经纠正了这种行为(https://www.macrumors.com/January 2022/27元/iOS Kramp-Karrenbauer 15元Kramp-karren Bauer4Kramp-karren Bauer Apps Kramp-karren Bauer 120元-Hz-Promotion/)。经笔者核实,仍需增加配置项,本文内容仍适用。
本文介绍了在iPhone上适配ProMotion的动态帧速率所观察到的现象和问题,试图推测其背后的原理,并讨论了解决问题的可能思路。最后,根据研究结果,对国际短视频业务上线方案进行了优化,并获得了核心业务指标的收益。
帧率是多少?在深入探讨推广屏带来的变化之前,我们先来回顾一个似乎很熟悉的概念:
帧率是多少?
众所周知,显示器无法显示真实的动态画面,所有的动画效果都是高速播放一帧帧静态画面欺骗人类视觉而产生的错觉。那么帧率最基本的定义就是屏幕内容的变化频率,这是一个物理指标。这种变化的频率由以下两个值决定:
刷新帧率:由屏幕硬件规格控制。传统的显示设备一般是59.94Hz,这决定了帧率的上限。渲染帧率:帧率下限由CPU Kramp-Karrenbauer GPU渲染流水线的执行速率控制决定。理想情况下,渲染帧率和刷新帧率应该完美匹配,或者渲染帧率应该是刷新帧率的整数倍,这样实际显示的内容才不会出现异常。但在现实中,这两者往往是不匹配的,卡顿就是其中之一:
当CPU Kramp-Karrenbauer GPU的渲染流水线遇到瓶颈,某一帧的渲染时间长于屏幕的刷新间隔时,前一帧会在屏幕上多停留几帧。当这个停留时间太长时,用户感知到屏幕更新的延迟,这就是所谓的卡顿。这也是iOS开发过程中遇到的主要性能问题之一。
实际帧速率与刷新率不同,它与显示的内容密切相关:
当显示静态图片时,理想情况下,只需要渲染一次。虽然屏幕仍然以60Hz或更高的频率刷新,但每次刷新显示的内容(FrameBuffer)并没有变化,用户感知的实际帧率仍然接近0元。在显示固定帧率的元素时,比如24元FPS的电影视频,用户感知的实际帧率自然是24元FPS左右。展示超高帧率内容,比如CS:GO运行200元FPS不锁帧。但是由于显示设备刷新率的限制,用户感知到的帧率仍然不会超过硬件帧率的上限。什么是动态刷新率提升?它本质上是自适应同步Kramp-Karrenbauer同步显示标准的一种实现。参考文献: https://en.wikipedia.org/wiki/Variable_refresh_rate
根据苹果官方文档,推广屏支持的刷新率是可变的。具体来说,对于iPhone:
iPhone 13 Pro和iPhone 13 Pro Max促销显示器可以使用以下刷新率和时间在显示器上显示内容:120赫兹(8毫秒)、80赫兹(12毫秒)、60赫兹(16毫秒)、48赫兹(20毫秒)、40赫兹(25毫秒)、30赫兹(33毫秒)、24赫兹(41毫秒)、20赫兹(50毫秒)、16赫兹(62毫秒)、15赫兹(66毫秒)、12赫兹(83毫秒)、10赫兹(100毫秒
对于iPad Pro:
iPad Pro的促销显示屏可以使用以下刷新率和时间在显示屏上显示内容:120赫兹(8毫秒)、60赫兹(16毫秒)、40赫兹(25毫秒)、30赫兹(33毫秒)、24赫兹(41毫秒)
其实这是苹果的VESA定制的Adaptive-Sync Kramp-karren Bauer Sync技术标准的一个实现,在游戏行业已经安装了很多年。类似的实现包括
减少停滞感。对于刷新率固定的屏幕,当某一帧渲染时间异常,在VSync信号到达后才完成渲染时,当前内容会停留在屏幕上,这个帧需要再次等待VSync信号才能渲染显示给用户。Adaptive-Sync Kramp-Karrenbauer同步技术可以避免这一点,渲染后尽快显示帧,从而减少显卡的突长:
降低移动设备的屏幕功耗。在配备固定刷新率屏幕的设备上,显示静态内容或低帧率内容(如视频)时,GPU的渲染频率会低于实际频率刷新率。但是固定刷新率的屏幕还是会以最高的速率刷新,重复显示之前的内容,造成额外的功耗。在这种情况下,推广屏可以主动降低刷新率和屏幕功耗,这对于移动设备尤为重要。
动态刷新率的表达iPhone 13元Pro、iPhone 13元Pro Max、iPad Pro促销显示屏能够在:快刷新率高达10Hz慢刷新率低至24Hz或10Hz之间动态切换
已知推广屏的刷新帧率不是固定的,系统会根据当前显示内容的类型和状态,实时动态切换屏幕的刷新帧率。为了更好地理解这种动态帧率的表达方式,笔者分别
Xr Kramp-Karrenbauer无促销iPhone 13元Pro Kramp-Karrenbauer有促销默认锁频。对一些典型的渲染场景进行测试,发现当App在配备推广屏的设备上运行时,不同场景下各种统计口径的帧率指标确实出现了有趣的变化。具体来说,作者处于以下情景中:
测试场景静态页面静态UIView,无动画/视频等元素密室逃脱:冠军联赛。滑动页面包含静态单元格的UITableView,只观察滑动时的性能。侏罗纪世界3。核心动画默认刷新率动画显示基于Cabasicanization的简单位移动画。4。核心动画120元Hz高刷新率动画只在推广设备上测试,基于卡巴斯基化的简单位移动画。同时,CadisableMinimum framework on phone和preferredFrameRateRange的帧速率限制解锁。(这一限制将在下文详细说明)5。Metal使用基于MTKView的播放器渲染30Hz/60Hz视频,分别播放源帧率为30Hz/60Hz的视频文件,并使用以下统计帧率指标进行测试:
测试指标CADisplayLink计算帧率,这是iOS中帧率的主要统计方法。根据CADisplayLink头文件中的描述。Angel Liu,cadisplaylink是一个“表示绑定到显示器vsync的计时器的类”。比较回调中当前帧/前一帧的时间戳,可以计算出前一帧的渲染时间(ts),其倒数(1元/TS)就是当前的实时帧率。冠军联赛。xcode GPU报告帧速率Xcode Kramp-karren Bauer显示调试导航器Kramp-Karrenbauer FPS中显示的帧速率。这只能统计当前应用直接通过OpenGL ES或者Metal绘制的帧率,比如游戏渲染/视频播放,而不能统计核心动画的帧率(众所周知,后者通过backboardd泷泽萝拉绘制)。《侏罗纪世界3》中核心动画FPS工具显示的帧率。乐器核心动画FPS乐器。这个统计就是核心动画的帧率,也就是渲染服务器backboardd进行泷泽萝拉渲染的频率。目前该工具存在一个BUG,无法显示高于60元FPS的帧率。4。仪表显示/垂直同步信号频率仪表中显示工具显示的表面/垂直同步信号的时间戳。如下图所示:
显示:指对应显示器的单面屏幕持续时间,CPU Kramp-Karrenbauer的GPU流水线渲染频率VSync:指垂直同步信号的时间戳,对应屏幕硬件的刷新频率在60Hz屏幕上。iOS设备默认采用双缓冲刷新机制,即前帧缓存和后帧缓存。GPU总是在后台帧缓存中绘制当前帧。当VSync信号到达时,交换前后帧的交换帧缓冲区,屏幕刷新以显示新内容。当屏幕以120Hz元Hz显示内容时,iOS会切换到三缓冲刷新机制(见上图三色面),这减轻了渲染管道的压力,但同时增加了渲染屏幕的延迟。
金属应用可以在120Hz元的Hz屏幕上通过设置cametalayer setmaximumdrawablecount 3360曹政奭在Kim Hye Yoon,Kramp-karren Bauer as Escape Room:Champions锦标赛中强制启用双缓冲机制来避免这种延迟。
如果屏幕显示内容不变,则不会更换Surface。一个Surface的显示可能会持续几个VSync间隔,但是额外的VSync信号仍然代表硬件层额外的屏幕刷新,导致额外的功耗。
非推广设备我们先来看看传统固定刷新率设备的情况。
VSync信号间隔固定为16.67msXR,屏幕刷新率固定为60Hz。与此对应的具体指标是VSync信号间隔。在任何场景下,XR的VSync信号间隔都固定在16.67 ms,另外在显示静态内容时,由于视图层树没有变化,核心动画中不会有新的事务提交,backboardd的泷泽萝拉也不会刷新,所以这个帧对应的面很长时间(几十秒)没有交换,核心动画FPS的值显示为0元。但由于VSync信号仍以60Hz的频率连续触发,此时屏幕重复显示的是同一个帧缓冲区,额外耗电。
CADisplayLink基本上完全跟随VSync信号。根据以往对iOS系统的了解,我们知道CADisplayLink是由VSync信号驱动的:
默认CADisplayLink的回调应该和VSync信号基本相同。这一点已经在XR上得到验证,并且通过用仪器记录主要过程的发生获得了以下结果:
其中包括:
runloop的第一行记录的是等待Kramp-Karrenbauer之后到等待之前每次运行循环的间隔,tick的第二行记录的是默认配置的CADisplayLink回调之间的间隔。底部是硬件显示/VSync事件的时序图。可以观察到以下现象,这与我们之前对DisplayLink的理解是一致的:
在没有卡顿的情况下,VSync信号和RunLoop的唤醒和CADisplayLink回调的触发是严格一一对应的。RunLoop卡住,无法处理源1元信号,DisplayLink回调延迟到卡住结束。在此过程中,VSync信号间隔保持不变。看看下面推广设备的测试结果。
VSync信号间隔是可变的。在升级屏幕上,VSync信号间隔是可变的,具体来说:
显示静态内容时,屏幕频率降低,最低频率刷新为10Hz。显示核心动画时,系统会适应动画的帧率设置,改变刷新率*你可以通过首选帧率设置高刷的提示请求,但不一定生效。具体见下面动态帧率的应用场景。
滑动内容时,刷新率在80Hz左右波动,并随滑动速度而变化。快速滑动时,刷新率增加,慢速滑动时,刷新率降低。显示视频时,刷新率和视频帧率是一致的。可以看出,VSync信号间隔可以主动跟随显示内容的渲染帧率的变化。
减少因卡顿造成的显示延迟。当主线程卡顿,某一帧的渲染在滑动中耗时过长时,系统会改变该帧对应的VSync信号间隔(下图中的表面5),以减少渲染到呈现的延迟,从而减缓用户感知的卡顿时间。
DisplayLink不完全跟随VSync信号。该图显示了CADisplayLink回调和Di的比较记录
第三个箭头所指的DisplayLink回调不及时。在此之前,主线程的caton已经结束,RunLoop执行了两次,但是直到第三次才调用DisplayLink的回调。不仅时序不匹配,还存在收到VSync但没有触发DisplayLink回调(且主线程空闲)的情况,如上图所示。解除DisplayLink的框架限制。我们知道,在iOS 15元上,苹果对第三方应用的显示帧率进行了默认限制。第三方应用需要在Info.plist中添加cadisableminimum education on phone字段才能解锁120Hz元Hz的刷新率。同时,在iOS 15元中,CADisplayLink等动画相关API也增加了配置首选帧率的属性:
/*定义此显示链接所需的回调速率范围,单位为每秒帧数。如果该范围包含相同的最小和最大帧速率,则该属性与preferredFramesPerSecond相同。否则,实际回调速率将被动态调整,以更好地与其他动画源保持一致。*/@ property(nonatomic)caframeterange preferredFrameRateRange API _ AVAILABLE(IOs(15.0),watchos(8.0),tvos(15.0));为了进一步探索新设备上DisplayLink和VSync信号之间的关系,作者解除了测试App核心动画的帧率限制,配置了相应的API,并在不同场景下重新测试:
场景动画显示动态内容的场景以中等速度显示一个位移动画,得到下图:
可以直观的发现,DisplayLink解锁帧率后的屏幕刷新率基本稳定在120Hz元Hz。而且VSync和DisplayLink的关系似乎又是一对一了。然而,当动画速度变慢时,作者发现这种对应关系发生了变化:
可以观察到,播放慢速动画时,DisplayLink的频率仍然是配置的120Hz元Hz,但实际屏幕刷新率只有30Hz。
滑动场景让我们在另一个场景中再次测试它,快速滑动视图,并在仪器中获得下图:
可以发现,DisplayLink解锁帧率后,屏幕刷新率基本稳定在120Hz元Hz,只有掉帧时,频率才降低。
需要注意的是,在CADisplayLink的回调中,作者除了调用OS _ sentag来报告日志之外,没有任何UI变化。即使笔者展示的TableView极其简单,上图还是可以观察到丢帧的情况,120Hz元Hz在滑动中也无法完美稳定。这可能说明UIKit的渲染性能在120Hz元Hz会有一些原生瓶颈。然后降低滑动屏幕的速度,结果类似于慢速动画。虽然DisplayLink的回调速度没有降低,但是VSync信号的频率一直保持在较低的水平:
Caton场景中的两个测试接近理想情况,即整个渲染循环执行几乎没有Caton的延迟。然而在现实中,应用程序的运行总是会出现各种或大或小的卡顿问题。为了验证更接近真实情况的DisplayLink和VSync信号的关系,作者人为地在连续滑动的情况下加入了一个20元MS的微小卡顿:
从上图可以看出,推广屏很好的处理了这个番茄酱。由于三种缓冲机制的存在,在Render Loop渲染Surface4番茄酱的过程中,系统试图通过改变VSync间隔来延迟缓冲区中Surface 283和Surface 250元的显示,从而尽可能缩短用户看到静止画面的时间。然后主线程恢复执行,可以看到DisplayLink的回调频率很快回到了卡顿之前的高水平。此时,由于前述卡顿减轻机制的存在,VSync信号的频率实际上降低了。此时,两个频率不匹配。这和前面播放慢动画/慢滑动的情况很像。因为卡顿和缓冲机制的存在,系统在短时间内降低了屏幕的刷新频率,但在CPU端仍然保持了DisplayLink的高速回调,满足了用户对API preferredFrameRateRange的设置。为了进一步分析这一机制的本质,笔者将尝试逆向分析iOS 15元中与系统库实现相关的变化。
分析反向显示链接驱动方式的变化,在CADisplayLink的回调方法上设置断点,分别在iOS 14元和15元推广设备上运行,可以得到:
在iOS 14元上,CADisplayLink由VSync信号通过Source 1 mach_port直接驱动。在iOS 15推广设备上,CADisplayLink不再由VSync信号驱动。它由15元的UIKit内部的Source0元信号驱动。当CADisplayLink第一次被创建并添加到RunLoop时,它会注册一个源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调试后,分步分析显示后续调用函数为ca :3360 display 3360 display link :3360 callback,其按键反汇编代码如下图所示:
看反汇编代码可以发现,如果ca : display _ link _ will _ fire _ handler的块返回NO,那么这个VSync信号回调就不会触发33603360 dispatch _ items的后续调用。实际上,这在LLDB中也得到了验证:
注意上图中的_ CFRUNLOOPCURENTISMAIN接近上图中的红框代码,后续的blraa指令明显调用了一个块(上面的LDRX9 Kim Hye Yoon X8,#0x 10元曹政奭的意思是把invoke指针拿出块结构)。tbz指令中的w0寄存器是块执行的返回值,为0元时跳转到0x1848dbc08(即NO),0x1848dbc08只是跳过dispatch_items的调用。通过上图中的blraa指令步骤,我们发现这个块实际上是由UIKitCore注册的:
找到了引用该符号的UIKit的私有方法__UIUpdateCycleSchedulerStart,反汇编结果也验证了这一点。
同时发现这个块的返回值固定为0x0元。
同样的符号在之前的iOS版本中并不存在,也就是说这应该是iOS 15元的变化。换了iOS 15元安装的非推广设备,重复上述逆向过程。发现此设备的ca :3360 display _ Link _ Will _ Fire _ Handler为空,并且未注册:
这里cbz进行了跳转,表示x0为零,x0由LDRX0,金惠允X8,#0x1C,8元曹政奭获得。
可以看到x0是ca :3360 display _ link _ will _ fire _ handler。继续分析之前发现的私有符号_ _ _ UIUpdateCycleSchedulerStart的相关实现,可以知道这是由于_ UIUpdateCyclineEnabled在非提升设备上返回no造成的。
如果返回NO,则不会执行_ __UIUpdateCycleSchedulerStart方法,也不会注册ca :3360 display _ link _ will _ fire _ handler。
_UIUpdateCycleEnabled带来的变化继续研究_UIUpdateCycleEnabled相关的代码。我发现这个变化并不是仅仅影响DisplayLink的驱动方式那么简单。当_UIUpdateCycleEnabled返回YES时,UIKit将在UIApplicationMain中执行_UIUpdateCycleSchedulerStart。对该函数的分析表明,当_UIUpdateCycleEnabled启用时,将调用Kim Hye Yoon ca transaction setdisablerunloopservercommits : yes曹政奭。
核心动画是大多数iOS应用程序的渲染引擎。熟悉iOS渲染过程的同学一定知道,它的执行也是由MainRunLoop驱动的,大致如下:
MainRunLoop由用户操作/定时器/GCD等唤醒。并调度相应的事件/回调来修改层树,触发setNeedsLayout或setNeedsDisplayMainRunLoop来完成此执行。Mainrunloop Observer,在BeforeWaiting事件中触发核心动画的注册BeforeWaiting,在即将进入睡眠状态前分发到Observer,触发事务提交ca :3360 transaction 33603360 commit():自上而下触发布局/显示等各种逻辑,更新布局/内容核心动画将更新后的图层树打包发送到渲染服务器5。然后MainRunLoop在6元内睡觉。渲染服务器解码打包的图层树,生成并提交相应的DrawCalls 7元。GPU执行渲染指令。渲染FrameBuffer,当后续的VSync信号到来时,会显示在屏幕上显示上图中的金惠允CA事务SetdisablerunLoopServerCommitments 3360 Yes曹政奭。这个调用给了作者一个提示,我们来验证一下ca : transaction 33603360 commit()在iOS 15推广设备上的执行时序,我们会发现它不再由BeforeWaiting事件驱动:
其实同一个源0元信号也带动了CADisplayLink的回调:
注意Source0元的回调符号runloopSourceCallback,你会发现Source 0元是由signalChanges函数驱动的:
SignalChanges由多个回调驱动:
其中包括:
RunloopObserverCallback是一个MainRunLoop observer驱动程序,在等待之前。RunloopTimerCallback由mk_timer驱动,其对应的mach_port未知。测试发现它的回调频率在1Hz左右,但会不断变化。我猜这是某种系统定时器。InputGroupSignaledCallback由mk_timer驱动,对应的mach_port是VSync信号。4。RequestRegistrySignAledCallback在即将开始滑动时由UIScrollView驱动。
通过以上分析,笔者有理由认为iOS 15元上应用的渲染驱动机制有了很大的改变。其中之一就是DisplayLink驱动源的变化。
结论iOS 15元上的苹果改变了推广设备中渲染事件周期的驱动方式。CoreAnimation的事务提交不再由RunLoop驱动,而是涉及多个信号源系统的动态帧率选择机制。会综合考虑用户设置的API(比如首选帧率)和实际显示内容的变化频率。就CADisplayLink而言,当内容低速变化时,CADisplayLink解锁的高刷新率只影响自身的回调频率,系统仍可能选择较低的屏幕刷新率,以减少耗电内容的高速变化。当CADisplayLink解锁高刷新率时,系统可以选择更高的刷新率,甚至可以实现锁定的120Hz元Hz的刷新。至于如何定义低速/中高速,笔者在下面CAAnimation的设置动态帧率部分做了一些实验,可以作为参考。
同时,CADisplayLink的默认配置回调频率高达60Hz,因此无法监控更高频率的刷新事件。在侏罗纪世界3中。推广设备,DisplayLink不再由VSync信号直接驱动,而是在新引入的渲染事件循环中执行。新版iOS系统实现了更复杂的机制,尽可能的回调以满足用户的偏好频率,但并不能保证其与VSync信号的强相关性。这意味着默认CADisplayLink的回调频率与实际帧率不匹配,之前基于CADisplayLink的帧率监控方案在推广设备上已经不可行。
动态帧率流畅度性能的应用场景监控动态帧率CADisplayLink在业界一般用于监控应用的流畅度。由于CADisplayLink在iOS 15元上的行为变化,原有的监测方案无法评估推广屏超过60Hz时的性能。根据上述探索结论,目前笔者设想了三种提升装置的兼容性改造方案:
第一种方案,金惠允[Pass]曹政奭,对任何设备都以60Hz为优化目标,只考虑刷新间隔大于16.67 ms的情况,换句话说,当屏幕以120Hz元Hz刷新时,即使1元帧掉线,也认为不掉线,因为两帧之间的间隔仍然小于
方案简单,只需将preferredFramesPerSecond设置为固定值60元即可兼容前面的指标。仍然可以计算FPS指数。当刷新率高于60Hz时,一般认为刷新率为60Hz。缺点:
由于只能监测最高60Hz,无法评估在更高刷新率下丢失的一些微小帧对用户体验的影响,也无法评估高屏上一些优化的技术影响。在低刷新率下,MainRunLoop仍然会运行在60Hz,对功耗有一定影响。方案二:Kim Hye Yoon【Pass】曹政奭通过一些手段,可以替代Source 1元信号驱动display_timer_callback的回调,用它来精确监听VSync信号,从而实现对动态帧率的精确监听。优势:
理论上,最精确的监控方案对功耗的影响最小,只有当屏幕刷新率实际增加时,回调频率才会增加。缺点:
使用私有APIFPS的FPS索引不再适用。VSync信号目前与渲染过程不完全匹配,虽然准确但不一定实用。方案三:Kim Hye Yoon [Pick]曹政奭确认CADisplayLink回调中的duration参数,计算当前屏幕的实时刷新率,修改preferredFrameRateRange进行跟踪。优点:方案比较简单,只需在每次回调中更新DisplayLink对象的preferredFrameRateRange属性即可。缺点:
由于动态帧率的存在,FPS指标可以反映实时的屏幕刷新,但聚合后意义不大。消费时,需要区分具体的车型/场景。据观察,目前的最低回调频率为60Hz,也就是说,不能确认在刷新率较低的情况下,48Hz、30Hz甚至更低刷新率的推广屏的性能仍然会运行在60Hz。对功耗有一定影响。请注意,CADisplayLink的preferredFrameRateRange需要以类似以下的格式设置:
NSInteger current fps=(NSInteger)ceil(1.0/display link . duration);display link . preferredframeraterange=caframeterangemake(10.0,currentFPS,0.0);CAFrameRateRange.minimum的最小值为10.0,首选值传到0.0元,这样CADisplayLink就只能用来监控当前系统帧率,而不影响帧率的动态选择。
与前两种方案相比,第三种方案改动小,没有私有API,监测精度高,缺点相对可以接受。
考虑到FPS指标不再直接关系到应用在推广画面上的流畅运行,其合计值参考价值不大,需要寻找新的指标作为替代。苹果官方在《Kramp-Karrenbauer,20元》中用XC测试引入了WWDC 10077精英动画Hitches中的Hitch Time Ratio概念,并强调比FPS更能适应不同刷新率的场景。在XCTest的框架中,苹果提供了API XCTOSSignpostMetric来帮助开发者在单次测试中获得指标,但是相关的API是在单次测试中提供的,不能在线使用。MetricKit中的MXAnimationMetric虽然可以在线获取,但不具备实时性,无法满足大型app对不同场景的监控需求。因此,请遵循以下Apple对挂接比率的定义:
故障时间:帧延迟显示的时间(毫秒)。故障时间比率:给定持续时间内的故障时间,单位为毫秒/秒。
我尝试实现了基于CADisplayLink的(滚动)挂接时间比的计算方案:
计算前一帧的帧时间戳和前一帧的目标帧时间戳,得到前一帧的挂接时间。确定是否在滑动中渲染和累积帧,以获得整个挂接帧。与累积的帧间隔相比较,滚动以获得Hitchtime比率。在测试过程中,笔者发现系统App在最高120Hz元Hz刷新率下运行稳定:
但是,即使CadisableMinimum教育手机设置为true,第三方App也无法以全帧率稳定滑动(已经验证在iOS 15.4 beta系统上依然如此)。借助iOS 15元推出的新API,可以在滑动、转场、动画等关键场景中主动解锁较高/较低的动态帧率,从而优化流畅度或动力,提升用户体验目标。
首先,笔者希望非系统App也能尽可能做到滑动稳定方面的120HzHz元Hz刷新。结合上面的分析,这可以用CADisplayLink来实现。笔者在此提出两种可能的方案,仅供参考:
创建一个CADisplayLink,将其首选FrameSite配置为120元,然后添加到UITrackingRunLoopMode。CADisplayLink *dp=.DP . preferred frames persecond=120;//或者DP . preferred frame rate=caframeterangemake(120.0,120.0,0.0元);[DP addtorunloop :[NSRunLoop mainRunLoop]formode : uitrackingrunloopmode];滑动过程中激活CADisplayLink,系统将当前帧率锁定在最高120Hz元Hz(仅在内容高速变化时有效)。当滑动停止时,恢复正常帧速率。
将CADisplayLink添加到CommonModes中,在开始/停止滑动时启用/暂停CADisplayLink,并修改preferredFramesPerSecond等相应属性来触发帧率变化。CADisplayLink *dp=.dp.paused=YES[DP addtorunloop :[NSRunLoop mainRunLoop]formode : nsrunloopcommonmodes];cfrunloopaddobserver(cfrunloopgetmain(),cfrunloopobservercreatewithandler(kcfallocatordefault,kCFRunLoopEntry | kCFRunLoopExit,YES,0,^(CFRunLoopObserverRef观察者,cfrunloopactivity activity){ if(activity==kcfrunloopentry){ DP . paused=no;DP . preferredframepersecond=120;} else { dp.paused=YESDP . preferredframepersecond=0;} })、(_ _ bridge CFStringRef)UITrackingRunLoopMode);实际中也有需要在非滑动状态下解锁帧率上限的情况,所以《方案密室:冠军联赛》的通用性会更好。
设置CAAnimation的动态帧率目前苹果只提供修改CAAnimation动画帧率的API。正在设置CaAnimation。首选帧速率可以改变其对屏幕刷新率的影响。
对于明显的用户感知,比如转场动画,可以设置为120Hz元Hz。如果感知不明显,比如旋转动画,可以降低帧率,比如设置为30Hz。不过和DisplayLink一样,上述API的设置也会“影响”系统动态帧率的选择,但这种影响并不是绝对的。在实际使用中,笔者发现屏幕选择的刷新率与CAAnimation在屏幕上变化的速度有关。对此,笔者以iPhone 13元Pro为例,用一个简单的120Hz元Hz的翻译动画,固定的首选帧率来说明:
caba sicanimation * anim=[caba sicanimation animation with key path : @ ‘ transform . translation . y ‘];CGFloat速度=170.0/330.0;anim . to value=@(100);anim . from value=@(0);anim.duration=10.0anim . repeat count=FLT _马克斯;anim . preferredframeraterange=caframeterangemake(120,120,120);速度的变量是平移的速度,单位是PT/s,实验表明:
拿(0元,曹政奭,160元,屏幕刷新率在金惠允60元HZ速度161,在曹政奭80元HZ速度[321,]320元,屏幕刷新率120Hz。我只测试了iPhone 13 Pro上的平移动画场景,以上数据仅供参考。最后,对于其他常见的动画API,如UIView.animateWithDuration、UIViewPropertyAnimator等。没有相应的API进行修改。理论上也可以通过某种手段获取这些上层API创建的CAAnimation对象来实现修改。
手势/转场等其他场景可以解锁120Hz元Hz。其他需要控制动态帧率的场景,也可以通过手动修改CADisplayLink的Preferred Frame Rate/Preferred Frame Rate属性来实现,这与通过监控RunLoop来修改滑动帧率基本相同。UIGestureRecognizer常用于实现交互式动画。经过测试发现,在触发手势回调的同时激活一个频率未锁定的CADisplayLink,可以间接增加UIGestureRecognizer的回调频率,从而实现更高帧率的交互动画。对于转场的场景,一个简单的方案就是swizzle UIViewController的生命周期消息,在出现/消失的节点启用/禁用CADisplayLink帧率解锁,从而实现一个通用的页面转场动画帧率解锁方案。
Flutter官方还计划提供一个类似的API,以便应用程序端可以针对不同的场景(滑动、动画等)动态切换屏幕刷新率:https://github.com/flutter/flutter/issues/90675
基于以上思路,作者团队在国际短视频业务中落地了一个优化项目,并通过实验进行了验证:
市场的滑动帧率P50从81.57提升到112.2,核心业务指标有所收益。结语近年来,苹果生态系统的软硬件发展日新月异,包括软件层dyld的不断优化,iOS 15元推出的新预览机制,新的推广屏。可见苹果一直致力于打造更流畅的用户体验。苹果提供的系统级优化方案一般是通用的,没有感知,但通用往往意味着一定的局限性,可能会预留额外的优化空间,应用开发者可以进一步研究如何更好的适应。比如本文,作者通过研究新推出的推广屏背后的机制,透过表象/深入装配管,看到了一部分本质,最终落地了监控优化方案,使得市场滑帧率P50从80元上升到112左右,实现了额外的商业效益。最后,笔者认为,作为苹果生态链中的一环,我们普通开发者在享受系统级优化自动带来的好处的同时,也应该主动去了解上述优化背后的底层原理。一方面,了解和学习苹果成熟的优化思路,可以提升我们作为工程师的视野。另一方面,对系统底层原理的理解可以扩大我们的“弹药库”。对商业价值传递的整个链条了解的越广、越深,就越有可能抓住潜在的优化点,从而在性能优化工程师的职业道路上走得更远、更好。
参考20元Kramp-Karrenbauer 10077精英动画与结缘
WWDC21 – 10147针对可变刷新率显示器进行优化https://developer . apple . com/videos/play/wwdc 2021/10147/
优化iPhone 13 Pro和iPad Pro https://developer . apple . com/documentation/quartz core/optimizing _ ProMotion _ Refresh _ Rates _ for _ iPhone _ 13 _ Pro _ and _ iPad _ Pro language=objc
什么是自适应同步https://www . viewsonic . com/library/tech/explained/what-is-Adaptive-Sync/
加入我们https://github.com/flutter/flutter/issues/90675.我们是字节国际化短视频的基础技术团队,一个追求深度完美的团队。我们专注于性能、架构、包大小、稳定性、自动化测试、基础库、编译构建等方面的深耕。以确保R & ampd超大规模团队的效率和全球数亿用户的体验。目前上海、杭州、新加坡、美国都有大量的人才。欢迎有志之士与我们一起打造全球十亿用户的APP!您可以点击“链接”进入字节跳动招聘官网提交简历,也可以通过邮件联系kazec.liu@bytedance.com获取相关信息或直接发送简历进行内部推送!

其他教程

兼职手工骗局(网上手工兼职骗局什么套路)

2022-8-17 18:00:49

其他教程

如何把录屏做成gif(手机录屏怎么做成gif图)

2022-8-17 18:02:53

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索