观看麻豆影视文化有限公司-国产 高清 在线-国产 日韩 欧美 亚洲-国产 日韩 欧美 综合-日日夜夜免费精品视频-日日夜夜噜

小米電池休眠自動解除(小米電池休眠自動解除打開后蓋)

  • 生活
  • 2023-04-19 14:20
  • 66
背景

Apple在今年推出了支持ProMotion屏幕的iPhone設(shè)備,讓App在iPhone13Pro和iPhone13ProMax上的最大刷新幀率可到達(dá)120Hz,極大優(yōu)化了應(yīng)用滑動/動畫的流暢度體驗。

ProMotion并不是一個新的概念,早在2017年,Apple推出的第二代iPadPro便搭載了這一刷新率最高可達(dá)120Hz的屏幕。在iPad上,高刷新率默認(rèn)對所有App啟用。而也許是出于能耗的考慮,在iPhone上,Apple并未將這個能力自動對所有App啟用,而是需要開發(fā)者手動添加配置項來進(jìn)行適配。

近期有消息指出iOS15.4beta修正了這一行為(https://www.macrumors.com/2022/01/27/ios-15-4-apps-120-hz-promotion/),經(jīng)過筆者驗證額外的配置項依然是需要的,并且本文內(nèi)容依然適用。

本文介紹了在iPhone上對ProMotion動態(tài)幀率的適配時觀察到的現(xiàn)象和遇到的問題,嘗試推測了背后的原理,并探討了解決問題的可能思路,最終基于調(diào)研結(jié)果在國際化短視頻業(yè)務(wù)上線優(yōu)化方案,取得了核心業(yè)務(wù)指標(biāo)的收益。

什么是幀率

在深入探究ProMotion屏幕所帶來的變化之前,我們先回顧一個似乎耳熟能詳?shù)母拍睿?/p>

什么是幀率?

眾所周知,顯示器并不能顯示真正動態(tài)的畫面,所有動畫效果都是靠高速播放一幀幀靜態(tài)畫面欺騙人類視覺所造成的假象。那么幀率最基本的定義便是屏幕內(nèi)容的變化頻率,是一個物理意義上的指標(biāo)。這種變化頻率又由以下兩個值共同決定:

刷新幀率:由屏幕硬件規(guī)格控制,傳統(tǒng)顯示設(shè)備一般為59.94Hz,決定了幀率的上限。渲染幀率:由CPU->GPU渲染管線的執(zhí)行速率控制,決定了幀率的下限。

理想情況下,渲染幀率和刷新幀率最好完全匹配,或者渲染幀率是刷新幀率的整數(shù)倍,這樣實際展現(xiàn)的內(nèi)容不會出現(xiàn)任何異常。但現(xiàn)實中二者往往會出現(xiàn)不匹配的情況,卡頓就是其中之一:

卡頓

當(dāng)CPU->GPU的渲染管線遇到瓶頸,導(dǎo)致某一幀的渲染耗時大于屏幕的刷新間隔時,上一幀畫面會在屏幕上多停留數(shù)幀的時間。當(dāng)這個滯留時間過長,用戶感知到畫面更新的延遲,這稱為卡頓。這也是iOS開發(fā)過程中會遇到的主要性能問題之一。

實際幀率

幀率并不等同于刷新率,它和所展示的內(nèi)容息息相關(guān):

展示靜態(tài)畫面時,理想情況只需要進(jìn)行一次渲染,盡管屏幕仍然以60Hz或者更高的頻率進(jìn)行刷新,每次刷新所展示的內(nèi)容(FrameBuffer)也未改變,用戶感知到的實際幀率依然接近0。展示固定幀率的元素,例如24FPS的電影視頻時,用戶感知到的實際幀率自然也是24FPS左右。展示超高幀率的內(nèi)容,例如CS:GO不鎖幀跑>200FPS,但由于顯示設(shè)備刷新率限制,用戶感知到的幀率依然不會超過硬件幀率的上限。什么是動態(tài)刷新率

ProMotion本質(zhì)上是對Adaptive-Sync顯示標(biāo)準(zhǔn)的一種實現(xiàn)。

Ref:https://en.***.org/wiki/Variable_refresh_rate

根據(jù)Apple官方文檔顯示,ProMotion屏幕支持的刷新率是可變的。

具體來說,對iPhone而言:

TheiPhone13ProandiPhone13ProMaxProMotiondisplayscanpresentcontentonthedisplayusingthefollowingrefreshratesandtimings:

120Hz(8ms),80Hz(12ms),60Hz(16ms),48Hz(20ms),40Hz(25ms),30Hz(33ms),24Hz(41ms),20Hz(50ms),16Hz(62ms),15Hz(66ms),12Hz(83ms),10Hz(100ms)

而對iPadPro來說:

TheiPadPro’sProMotiondisplaycanpresentcontentonthedisplayusingthefollowingrefreshratesandtimings:

120Hz(8ms),60Hz(16ms),40Hz(25ms),30Hz(33ms),24Hz(41ms)

這其實是Apple對VESA定制的Adaptive-Sync技術(shù)標(biāo)準(zhǔn)的一種實現(xiàn),在游戲業(yè)界已經(jīng)實裝多年,類似的實現(xiàn)還有AMD的FreeSync和Nivida的G-Sync。這種新的顯示技術(shù)有著以下優(yōu)點:

減少可感知的卡頓

對于固定刷新率的屏幕而言,當(dāng)某一幀的渲染耗時出現(xiàn)異常,在VSync信號到來之后才完成渲染,那么當(dāng)前內(nèi)容便會滯留在屏幕上,這一幀需要再等一次VSync信號才能被渲染展示給用戶。

而Adaptive-Sync技術(shù)可以避免這一點,在該幀渲染結(jié)束后盡快進(jìn)行展示,從而減少顯示卡頓時長:

減少移動設(shè)備的屏幕功耗

在搭載了固定刷新率屏幕的設(shè)備上,當(dāng)顯示靜態(tài)內(nèi)容或者幀率較低(例如視頻)的內(nèi)容時,GPU的渲染頻率比實際頻率刷新率會更低。但是固定刷新率的屏幕依然會已最高速率進(jìn)行刷新,重復(fù)展示之前的內(nèi)容,造成了額外的電量消耗。

ProMotion屏幕在這種情況下可以主動降低刷新率,減少屏幕功耗,這對于移動設(shè)備來說尤其重要。

動態(tài)刷新率的表現(xiàn)形式

TheiPhone13Pro,theiPhone13ProMax,andtheiPadProProMotiondisplaysarecapableofdynamicallyswitchingbetween:

Fasterrefreshratesupto120Hz

Slowerrefreshratesdownto24Hzor10Hz

已知,ProMotion屏幕的刷新幀率并不固定,系統(tǒng)會實時地根據(jù)當(dāng)前顯示內(nèi)容的類型和狀態(tài)來動態(tài)切換屏幕的刷新幀率。為了更好地理解這種動態(tài)幀率的表現(xiàn)形式,筆者分別在

iPhoneXR-無ProMotioniPhone13Pro-有ProMotion默認(rèn)鎖頻

上對一些典型渲染場景進(jìn)行了測試,發(fā)現(xiàn)搭載了ProMotion屏幕的設(shè)備上運行App時,不同的場景下的各種統(tǒng)計口徑的幀率指標(biāo)確實展示出了有趣的變化。

具體而言,筆者分別在以下幾種場景:

測試場景靜態(tài)頁面

靜態(tài)的UIView,無動畫/視頻等元素

2.滑動中的頁面

包含靜態(tài)Cell的UITableView,僅觀察滑動中的表現(xiàn)

3.CoreAnimation默認(rèn)刷新率動畫

顯示基于CABasicAnimation實現(xiàn)的簡單位移動畫

4.CoreAnimation120Hz高刷新率動畫

僅在ProMotion設(shè)備上測試,基于CABasicAnimation實現(xiàn)的簡單位移動畫,同時解鎖了CADisableMinimumFrameDurationOnPhone和preferredFrameRateRange幀率限制。(關(guān)于此限制下文會有具體介紹)

5.Metal渲染30Hz/60Hz視頻

使用基于MTKView進(jìn)行渲染的播放器,播放源幀率分別為30Hz/60Hz的視頻文件

并使用以下幾種統(tǒng)計口徑的幀率指標(biāo)進(jìn)行測試:

測試指標(biāo)CADisplayLink計算幀率

iOS中主要的幀率統(tǒng)計手段。

根據(jù)CADisplayLink.h頭文件中描述,CADisplayLink是一個”Classrepresentingatimerboundtothedisplayvsync“。在回調(diào)中比較當(dāng)前幀/前一幀的時間戳,可以計算出上一幀的渲染耗時(ts),其倒數(shù)(1/ts)即為當(dāng)前的實時幀率。

2.XcodeGPUReport幀率

Xcode->ShowDebugNavigator->FPS中顯示的幀率。這個只能統(tǒng)計當(dāng)前應(yīng)用直接通過OpenGLES或者M(jìn)etal進(jìn)行繪制的幀率,例如游戲渲染/視頻播放,無法統(tǒng)計CoreAnimation的幀率(眾所周知,后者通過backboardd進(jìn)行繪制)。

3.InstrumentsCoreAnimationFPS

Instruments中CoreAnimationFPS工具所顯示的幀率。這個統(tǒng)計的是CoreAnimation的幀率,即RenderServerbackboardd繪制的頻率。目前該工具有BUG無法顯示高于60FPS的幀率。

4.InstrumentsDisplay/VSync信號頻率

Instruments中Display工具所顯示的Surface/VSync信號時間戳。如下圖所示:

Display:指對應(yīng)顯示器的單個Surface上屏持續(xù)的時間,對應(yīng)CPU-GPU管線的渲染頻率VSync:指垂直同步信號時間戳,對應(yīng)屏幕硬件的刷新頻率

在60Hz屏幕上,iOS設(shè)備默認(rèn)采用雙緩沖刷新機制,也就是前幀緩存和后幀緩存。GPU總是在后幀緩存上進(jìn)行當(dāng)前幀的繪制。當(dāng)VSync信號到來時,交換前后幀緩存的指針(SwapFrameBuffer),屏幕刷新顯示新的內(nèi)容。

而當(dāng)屏幕以120Hz顯示內(nèi)容時,iOS會切換成三緩沖刷新機制(見上圖中三種顏色的Surface),這減少渲染管線的壓力,但同時會增加一定的渲染上屏延遲。

Metal應(yīng)用可以通過設(shè)置-[CAMetalLayersetMaximumDrawableCount:]為2來在120Hz屏幕上強制啟用雙緩沖機制,避免這種延遲。

如果屏幕顯示內(nèi)容未發(fā)生變化,Surface則不會發(fā)生交換,一個Surface的Display可能持續(xù)數(shù)個VSync間隔,但多余的VSync信號依然代表著硬件層額外的屏幕刷新,造成額外的電量消耗。

非ProMotion設(shè)備

首先讓我們看看傳統(tǒng)的固定刷新率的設(shè)備的情況。

VSync信號間隔固定為16.67ms

XR的屏幕刷新率為固定的60Hz,這一點對應(yīng)的具體指標(biāo)是VSync信號的間隔,而在任何場景下,XR的VSync信號的間隔均為固定的16.67ms。

此外,在顯示靜態(tài)內(nèi)容時,由于視圖LayerTree無變化,CoreAnimation不會有提交新的事務(wù)提交,backboardd不會進(jìn)行刷新,所以對應(yīng)這一幀的Surface也長時間(數(shù)十秒)未被交換下去,CoreAnimationFPS的值顯示為0。

但由于VSync信號仍然以60Hz的頻率持續(xù)觸發(fā),屏幕此時正在不停重復(fù)展示同樣的FrameBuffer,消耗了額外的電量。

CADisplayLink基本完全跟隨VSync信號

根據(jù)過去對iOS系統(tǒng)的認(rèn)知,我們知道CADisplayLink是由VSync信號驅(qū)動的:

默認(rèn)配置的CADisplayLink的回調(diào)應(yīng)該與VSync信號基本同時。

這一點在XR上得到了驗證,用Instruments記錄一次主線程發(fā)生的卡頓,得到:

其中:

第一行runloop記錄每次RunLoopAfterWaiting->BeforeWaiting的間隔第二行tick記錄默認(rèn)配置的CADisplayLink回調(diào)間的間隔最下面則是硬件Display/VSync事件時序圖

可以觀察到下述現(xiàn)象,符合我們之前的對DisplayLink的認(rèn)識:

沒有卡頓的情況下,VSync信號和RunLoop的喚醒&CADisplayLink回調(diào)的觸發(fā)嚴(yán)格一一對應(yīng)。RunLoop卡頓,無法處理Source1信號,DisplayLink回調(diào)被延遲到卡頓結(jié)束時。在此過程中VSync信號間隔始終保持不變。ProMotion設(shè)備

下面看看ProMotion設(shè)備的測試結(jié)果。

VSync信號間隔可變

在ProMotion屏幕上VSync信號間隔是可變的,具體而言:

顯示靜態(tài)內(nèi)容時,屏幕降頻,最低以10Hz的頻率進(jìn)行刷新顯示CoreAnimation動畫時,系統(tǒng)會適配動畫的幀率設(shè)置改變刷新率

*通過preferredFrameRateRange可以設(shè)置hint請求高刷,但并不一定生效,詳見下文“動態(tài)幀率的應(yīng)用場景”部分。

顯示滑動中內(nèi)容時,刷新率在80Hz左右波動,并且跟隨滑動速度變化而變化??旎瑫r刷新率升高,慢滑時降低。顯示視頻時,刷新率和視頻幀率維持一致

可以看到VSync信號間隔能主動跟隨顯示內(nèi)容的渲染幀率的改變而改變。

減少卡頓造成的顯示延遲

在主線程發(fā)生卡頓導(dǎo)致滑動中某一幀渲染耗時過長時,系統(tǒng)會改變這一幀所對應(yīng)的VSync信號間隔(下圖Surface5),減小從渲染到展示的延時,從而減緩用戶感知到的卡頓時長。

DisplayLink不完全跟隨VSync信號

如圖是一張滑動中場景的CADisplayLink回調(diào)和Display/VSync事件對照記錄。和之前不同的是,再ProMotion設(shè)備上DisplayLink和VSync信號之間沒有表現(xiàn)出明顯的跟隨關(guān)系:

具體而言:

第三個箭頭所指向的DisplayLink的回調(diào)并不及時。在這之前主線程的卡頓已經(jīng)結(jié)束,并且額外執(zhí)行了兩次RunLoop,但直到第三次才調(diào)用了DisplayLink的回調(diào)。不僅僅是時機不匹配,也存在收到VSync但不觸發(fā)DisplayLink回調(diào)的情況(并且主線程處于空閑狀態(tài)),例如上圖中的?處。解除DisplayLink的幀數(shù)限制

我們知道,在iOS15上Apple對第三方應(yīng)用的顯示幀率默認(rèn)做了限制。第三方應(yīng)用需要在Info.plist中添加<key>CADisableMinimumFrameDurationOnPhone</key><true/>字段才可以解鎖120Hz的刷新率。

于此同時,在iOS15中,CADisplayLink等動畫相關(guān)API也新增了一個用于配置偏好幀率的屬性:

/*Definestherangeofdesiredcallbackrateinframes-per-secondforthisdisplaylink.Iftherangecontainsthesameminimumandmaximumframerate,thispropertyisidenticalaspreferredFramesPerSecond.Otherwise,theactualcallbackratewillbedynamicallyadjustedtobetteralignwithotheranimationsources.*/@property(nonatomic)CAFrameRateRangepreferredFrameRateRangeAPI_AVAILABLE(ios(15.0),watchos(8.0),tvos(15.0));

為了進(jìn)一步探究新設(shè)備上DisplayLink和VSync信號之間的關(guān)系,筆者將測試App的CoreAnimation的幀率限制解除,并配置對應(yīng)的API,分別在不同的場景重新進(jìn)行測試:

顯示動態(tài)內(nèi)容的場景動畫場景

展示一個速度中等的位移動畫,得到下圖:

可以很直觀地發(fā)現(xiàn),DisplayLink解鎖幀率后的屏幕刷新率基本穩(wěn)定在120Hz。并且VSync和DisplayLink的關(guān)系似乎又重新一一對應(yīng)了起來。

但是,將動畫速度減慢,筆者發(fā)現(xiàn)這種對應(yīng)關(guān)系發(fā)生了變化:

可以觀察到在播放慢速動畫時,DisplayLink的頻率依然是配置的120Hz,但是實際的屏幕刷新率卻只有30Hz。

滑動場景

讓我們換一種場景再次進(jìn)行測試,快速滑動視圖,在Instruments中得到下圖:

可以發(fā)現(xiàn),DisplayLink解鎖幀率后,屏幕刷新率同樣基本穩(wěn)定在120Hz,僅在丟幀時有降頻。

需要注意的是筆者在CADisplayLink的回調(diào)中除了調(diào)用os_signpost上報log外無任何UI改動。即便筆者展示的TableView極其簡單,上圖中仍然可以觀察到丟幀,無法在滑動中完美穩(wěn)定120Hz。這也許說明UIKit的渲染性能在120Hz下會有某種程度上的原生瓶頸。

然后降低滑動屏幕的速度,得到了和慢速動畫相似的結(jié)果,盡管DisplayLink回調(diào)速度不減,但是VSync信號頻率一直保持在較低的水平:

卡頓場景

上面兩次測試都接近理想情況,即整個RenderLoop執(zhí)行幾乎沒有延遲與卡頓。但是現(xiàn)實中應(yīng)用的運行總是有著各種各樣的或大或小的卡頓問題。

為了驗證更接近現(xiàn)實情況下,DisplayLink和VSync信號之間的關(guān)系,在連續(xù)滑動的情況下筆者人為加入了一個20ms的微小卡頓進(jìn)行測試:

上圖中可以看到,ProMotion屏幕很好的處理了這次卡頓,由于三緩沖機制的存在,再RenderLoop渲染Surface4卡頓期間,通過改變VSync間隔,系統(tǒng)嘗試將緩沖區(qū)中的Surface283與Surface250延遲上屏,盡量縮短了用戶看到靜止畫面的時長。

隨后,主線程恢復(fù)執(zhí)行,可以看到DisplayLink的回調(diào)頻率很快恢復(fù)至卡頓前的高水平。而此時VSync信號由于前述卡頓減緩機制的存在頻率其實有所降低。此時二者頻率并不吻合。

這和之前播放慢速動畫/慢速滑動的情況很相似,由于卡頓加上緩沖機制的存在導(dǎo)致短時間內(nèi)系統(tǒng)將屏幕的刷新頻率降低,但在CPU側(cè)依然維持了DisplayLink的高速回調(diào),滿足了使用方對preferredFrameRateRange這一API的設(shè)置。

為了進(jìn)一步分析了這種機制的本質(zhì),筆者接下來會嘗試逆向分析iOS15中的系統(tǒng)庫相關(guān)實現(xiàn)的改動。

逆向分析DisplayLink驅(qū)動方式的變化

在CADisplayLink回調(diào)***上設(shè)置斷點,分別在iOS14和15ProMotion設(shè)備上運行,可以得到:

在iOS14上,CADisplayLink是通過Source1mach_port直接接受VSync信號驅(qū)動的在iOS15ProMotion設(shè)備上,CADisplayLink不再由VSync信號驅(qū)動,而是由一個UIKit內(nèi)部的Source0信號驅(qū)動

在15中,CADisplayLink第一次創(chuàng)建并添加至RunLoop的時候,會注冊一個Source1信號,這和14中行為一致。

其callout回調(diào)地址對應(yīng)符號為同樣為display_timer_callback,同樣和14中的一致。

這也可以解釋為什么15上VSync信號確實會喚醒一次RunLoop,只是這次喚醒并不一定觸發(fā)DisplayLink的回調(diào),這就說明display_timer_callback行為和14相比一定發(fā)生了某種變化。

display_timer_callback邏輯的變化

使用Hopper分析display_timer_callback的實現(xiàn),發(fā)現(xiàn)15和14的實現(xiàn)并無區(qū)別。使用LLDB進(jìn)行debug,逐步分析,觀察到后續(xù)調(diào)用函數(shù)為CA::Display::DisplayLink::callback,其關(guān)鍵反匯編代碼如下圖所示:

觀察反匯編代碼可以發(fā)現(xiàn),如果CA::display_link_will_fire_handler這個block返回了NO,則這次VSync信號回調(diào)不會觸發(fā)后續(xù)的CA::DisplayLink::dispatch_items調(diào)用。

實際上在LLDB中也驗證了這點:

注意上圖中的_CFRunLoopCurrentIsMain和上圖紅框代碼接近,后續(xù)的blraa指令看起來很明顯是調(diào)用了一個block(上面的ldrx9[x8,#0x10]就是把invoke指針從block結(jié)構(gòu)體中取出的意思)。tbz指令中w0寄存器為block執(zhí)行的返回值,為0(即NO)時跳轉(zhuǎn)至0x1848dbc08,而0x1848dbc08剛好在dispatch_items的調(diào)用之后,跳過了該調(diào)用。

通過對上圖中blraa指令stepin,我們發(fā)現(xiàn)這個block實際上是由UIKitCore注冊的:

找到引用了該符號的UIKit的私有***__UIUpdateCycleSchedulerStart,反匯編結(jié)果也驗證了這點。

同時發(fā)現(xiàn)這個block的返回值固定為0x0。

而同樣的symbol在之前的iOS版本上并不存在,也就是說這個應(yīng)該是iOS15的變動。換安裝了iOS15的非ProMotion設(shè)備,重走上面的逆向流程發(fā)現(xiàn),該設(shè)備的CA::display_link_will_fire_handler為nil,未注冊:

這里cbz執(zhí)行了跳轉(zhuǎn),說明x0為nil,而x0是由ldrx0,[x8,#0x1c8]得到。

可以看到x0就是CA::display_link_will_fire_handler。繼續(xù)分析之前找到的私有符號__UIUpdateCycleSchedulerStart的相關(guān)實現(xiàn),可以知道這是因為在非ProMotion設(shè)備上_UIUpdateCycleEnabled返回了NO導(dǎo)致的。

在返回NO的情況下__UIUpdateCycleSchedulerStart***不會執(zhí)行,CA::display_link_will_fire_handler也就不會被注冊。

_UIUpdateCycleEnabled所帶來的變化

繼續(xù)研究_UIUpdateCycleEnabled相關(guān)的代碼,筆者發(fā)現(xiàn)這個的改動并不是僅僅影響DisplayLink驅(qū)動方式那么簡單。

當(dāng)_UIUpdateCycleEnabled返回YES時,UIKit會在UIApplicationMain中執(zhí)行_UIUpdateCycleSchedulerStart。分析該函數(shù),發(fā)現(xiàn)_UIUpdateCycleEnabled啟用時會調(diào)用[CATransactionsetDisableRunLoopObserverCommits:YES]。

CoreAnimation是絕大部分iOS應(yīng)用的渲染引擎,熟悉iOS渲染流程的同學(xué)想必都知道它的執(zhí)行也是由MainRunLoop驅(qū)動,大致為:

MainRunLoop因為用戶操作/Timer/GCD等被喚醒,派發(fā)相應(yīng)的事件/回調(diào)回調(diào)中應(yīng)用修改LayerTree,觸發(fā)setNeedsLayout或setNeedsDisplayMainRunLoop即將完成本次執(zhí)行,在即將休眠前向Observer派發(fā)BeforeWaiting事件BeforeWaiting中觸發(fā)CoreAnimation注冊的MainRunLoopObserver,觸發(fā)事務(wù)提交CA::Transaction::commit():自頂向下觸發(fā)各種Layout/Display等邏輯,更新布局/內(nèi)容CoreAnimation將更新后的LayerTree打包發(fā)送給RenderServer

5.隨后MainRunLoop進(jìn)入休眠

6.RenderServer將打包好的LayerTree解碼,生成并提交對應(yīng)的drawcalls

7.GPU執(zhí)行渲染指令,渲染出FrameBuffer,待后續(xù)VSync信號來臨時上屏展示

上圖中+[CATransactionsetDisableRunLoopObserverCommits:YES]這個調(diào)用給了筆者提示,讓我們驗證一下CA::Transaction::commit()在iOS15ProMotion設(shè)備上的執(zhí)行時機,會發(fā)現(xiàn)確實不再由BeforeWaiting事件驅(qū)動了:

實際上同樣的Source0信號同時也驅(qū)動了CADisplayLink的回調(diào):

關(guān)注這個Source0的回調(diào)符號runloopSourceCallback,會發(fā)現(xiàn)這個Source0是由signalChanges函數(shù)驅(qū)動:

而signalChanges又是由多個回調(diào)所驅(qū)動:

其中:

runloopObserverCallback為一個BeforeWaiting的MainRunLoopobserver驅(qū)動。runloopTimerCallback由mk_timer驅(qū)動,對應(yīng)的mach_port不明,測試發(fā)現(xiàn)其回調(diào)頻率在1Hz左右,但也會不斷變化,猜測是某種系統(tǒng)計時器。inputGroupSignaledCallback由mk_timer驅(qū)動,對應(yīng)的mach_port正是VSync信號。

4.requestRegistrySignaledCallback由UIScrollView在即將開始滑動時驅(qū)動。

通過上面的分析,筆者有理由認(rèn)為在iOS15上應(yīng)用的渲染驅(qū)動機制出現(xiàn)了比較大的變化。其中之一便是DisplayLink的驅(qū)動源的改變。

結(jié)論iOS15上Apple改變了在ProMotion設(shè)備的渲染事件循環(huán)的驅(qū)動方式,CoreAnimation的事務(wù)提交不再由完全由RunLoop驅(qū)動,而是涉及了多個信號源系統(tǒng)動態(tài)幀率選擇的機制會綜合考慮使用方設(shè)置的API(如preferredFrameRateRange)和實際展示的內(nèi)容的變化頻率。具體對CADisplayLink而言:內(nèi)容低速變化時,CADisplayLink解鎖高刷新率僅影響自身的回調(diào)頻率,系統(tǒng)仍可能選擇較低的屏幕刷新率來降低功耗內(nèi)容中高速變化時,CADisplayLink解鎖高刷新率可以讓系統(tǒng)選擇更高的刷新頻率,甚至實現(xiàn)鎖定120Hz的刷新

關(guān)于如何界定低速/中高速,筆者在下文中CAAnimation設(shè)置動態(tài)幀率部分做了一些試驗,可作為參考。

同時,默認(rèn)配置的CADisplayLink回調(diào)頻率最高為60Hz,無法監(jiān)控更高頻率的刷新事件。

3.ProMotion設(shè)備中,DisplayLink不再由VSync信號直接驅(qū)動,而是在新引入的渲染事件循環(huán)中執(zhí)行。新版本iOS系統(tǒng)實現(xiàn)了某種更復(fù)雜的機制來盡可能滿足使用者設(shè)置的偏好頻率進(jìn)行回調(diào),但并不保證它與VSync信號的強關(guān)聯(lián)性。這意味著默認(rèn)的CADisplayLink的回調(diào)頻率與實際幀率并不匹配,之前基于CADisplayLink進(jìn)行幀率監(jiān)控的方案在ProMotion設(shè)備上變得不再可行。

動態(tài)幀率的應(yīng)用場景監(jiān)控動態(tài)幀率下的流暢度表現(xiàn)

業(yè)界中一般采用CADisplayLink對應(yīng)用的流暢度進(jìn)行監(jiān)控。由于CADisplayLink的行為在iOS15上的變化,原先的監(jiān)控方案無法評估ProMotion屏幕在超過60Hz時的表現(xiàn)。

根據(jù)上面的探索結(jié)論,目前筆者設(shè)想了三種針對ProMotion設(shè)備的兼容性修改方案:

方案一[Pass]

對于任何設(shè)備都以60Hz為優(yōu)化目標(biāo),只考慮刷新間隔長于16.67ms的情況。換句話說,在屏幕以120Hz刷新時,對于丟1幀的情況也認(rèn)為不丟幀,因為此時兩幀之間的間隔仍然小于16.67ms,理論上用戶感知不大。

優(yōu)點:

方案簡單,僅需設(shè)置preferredFramesPerSecond為固定值60即可兼容之前的指標(biāo)。依然可以計算FPS指標(biāo),對于刷新率高于60Hz的情況統(tǒng)一認(rèn)為刷新率為60Hz

缺點:

由于只能監(jiān)控最高60Hz的情況,無法評估更高刷新率下一些微小丟幀對用戶體驗帶來的影響,也無法評估對高刷屏的一些優(yōu)化所帶來的技術(shù)影響在低刷新率時,MainRunLoop依然會以60Hz運行,對功耗有一定影響方案二[Pass]

通過一些手段,可以替換驅(qū)動display_timer_callback的Source1信號的回調(diào),使用它來準(zhǔn)確監(jiān)聽VSync信號,實現(xiàn)對動態(tài)幀率的準(zhǔn)確監(jiān)控。

優(yōu)點:

理論上最精確的監(jiān)控方案對功耗的影響最小,回調(diào)頻率只有在屏幕刷新率實際升高時才會隨之提升

缺點:

使用了私有APIFPS指標(biāo)從此不再適用VSync信號目前和渲染流程不完全匹配,雖然精確但不一定實用方案三[Pick]

通過在CADisplayLink回調(diào)中確認(rèn)duration參數(shù),計算得到當(dāng)前屏幕的實時刷新率,并修改preferredFrameRateRange來進(jìn)行跟蹤。

優(yōu)點:

方案相對簡單,只需在每次回調(diào)中更新DisplayLink對象的preferredFrameRateRange屬性即可

缺點:

由于動態(tài)幀率的存在,F(xiàn)PS指標(biāo)可以反映實時屏幕刷新情況,但是聚合后的意義不大,消費時需要區(qū)分特定機型/場景觀察到目前的最小回調(diào)頻率為60Hz,也就是說無法確認(rèn)ProMotion屏幕在48Hz、30Hz甚至更低刷新率下的表現(xiàn)在低刷新率時,MainRunLoop依然會以60Hz運行,對功耗有一定影響

需要注意的是,CADisplayLink的preferredFrameRateRange需要以類似一下格式進(jìn)行設(shè)置:

NSIntegercurrentFPS=(NSInteger)ceil(1.0/displayLink.duration);displayLink.preferredFrameRateRange=CAFrameRateRangeMake(10.0,currentFPS,0.0);

CAFrameRateRange.minimum傳最小值10.0,preferred傳0.0,可以讓該CADisplayLink只用于監(jiān)控當(dāng)前的系統(tǒng)幀率,而不影響幀率的動態(tài)選擇。

相比前兩個方案,方案三改動小,不使用私有API,監(jiān)控準(zhǔn)確性也較高,缺點相對來說可以接受。

FPS的替代指標(biāo)

考慮到在ProMotion屏幕上FPS指標(biāo)不再與應(yīng)用運行是否流暢直接相關(guān),它的聚合值參考價值不大,有必要尋找一個新指標(biāo)作為替換。

Apple官方在WWDC20-10077EliminateanimationhitcheswithXCTest中介紹了HitchTimeRatio這一概念,并著重說明了它比單純的FPS更能適配不同刷新率的場景。

在XCTest框架中,蘋果提供了APIXCTOSSignpostMetric幫助開發(fā)者在單測中即時地獲取該指標(biāo),但相關(guān)API盡在單測中提供,線上無法使用。而MetricKit中的MXAnimationMetric盡管可以在線上獲取,但卻不是實時的,無法滿足大型App對不同場景的監(jiān)控需求。

因此,遵循下面Apple對HitchRatio的定義:

Hitchtime:

Timeinmsthataframeislatetodisplay.

Hitchtimeratio:

Hitchtimeinmspersecondforagivenduration.

筆者嘗試實現(xiàn)了基于CADisplayLink的(Scroll)HitchTimeRatio的計算方案:

計算上一幀的幀時間戳與上上一幀的目標(biāo)幀時間戳得到上一幀的HitchTime確定該幀是否是在滑動中渲染累計得到整體的HitchFrame,與累積的幀間隔相比,得到(Scroll)HitchTimeRatio關(guān)鍵場景提升幀率

在測試過程中筆者發(fā)現(xiàn),系統(tǒng)App滑動時是穩(wěn)定以最高刷新率120Hz運行的:

而第三方App即便設(shè)置了CADisableMinimumFrameDurationOnPhone為true也無法穩(wěn)定以滿幀率滑動(經(jīng)過驗證,這一點在iOS15.4beta系統(tǒng)上依然成立)。

通過利用iOS15引入的新API,我們可以在關(guān)鍵場景如滑動、轉(zhuǎn)場、動畫過程中主動解鎖更高/限制更低的動態(tài)幀率,從而優(yōu)化流暢度或者優(yōu)化功率,提升用戶體驗?zāi)繕?biāo)。

滑動中穩(wěn)定120Hz

首先,筆者希望非系統(tǒng)App也可以盡可能實現(xiàn)滑動中穩(wěn)定120Hz刷新。

結(jié)合上述分析,這一點可以用CADisplayLink來實現(xiàn)。這里筆者提出兩種可能方案僅供參考:

創(chuàng)建CADisplayLink,配置其preferredFramesPerSecond為120,然后將其添加到UITrackingRunLoopMode中。CADisplayLink*dp=...dp.preferredFramesPerSecond=120;//或者dp.preferredFrameRateRange=CAFrameRateRangeMake(120.0,120.0,0.0);[dpaddToRunLoop:[NSRunLoopmainRunLoop]forMode:UITrackingRunLoopMode];

在滑動中,該CADisplayLink被激活,系統(tǒng)鎖定當(dāng)前幀率為最高120Hz(僅在內(nèi)容中高速變化時生效)。停止滑動時則恢復(fù)正常幀率。

添加CADisplayLink至CommonModes中,分別在開始/停止滑動時啟用/暫停CADisplayLink,并修改對應(yīng)的preferredFramesPerSecond等屬性,觸發(fā)幀率變化。CADisplayLink*dp=...dp.paused=YES;[dpaddToRunLoop:[NSRunLoopmainRunLoop]forMode:NSRunLoopCommonModes];CFRunLoopAddObserver(CFRunLoopGetMain(),CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault,kCFRunLoopEntry|kCFRunLoopExit,YES,0,^(CFRunLoopObserverRefobserver,CFRunLoopActivityactivity){if(activity==kCFRunLoopEntry){dp.paused=NO;dp.preferredFramePerSecond=120;}else{dp.paused=YES;dp.preferredFramePerSecond=0;}}),(__bridgeCFStringRef)UITrackingRunLoopMode);

在實踐中,由于也存在需要在非滑動狀態(tài)下解鎖幀率上限的情況,所以方案2的通用性會更好。

CAAnimation設(shè)置動態(tài)幀率

目前蘋果只提供了修改CAAnimation動畫幀率的API,設(shè)置CAAnimation.preferredFrameRateRange即可改變其對屏幕刷新率的影響。

對于用戶感知明顯的,如轉(zhuǎn)場動畫,可以設(shè)置為120Hz。對于感知不明顯的,如旋轉(zhuǎn)動畫,可以降低其幀率,比如設(shè)置為30Hz。

但是,和DisplayLink相同,過上述API的設(shè)置雖然會“影響”系統(tǒng)的動態(tài)幀率的選擇,但這種影響并不是絕對的。在實際使用中,筆者發(fā)現(xiàn)屏幕選擇的刷新率和CAAnimation在屏幕上變化的速度有關(guān)。

關(guān)于此點,以iPhone13Pro為例,筆者使用了一個簡單的、偏好幀率為固定120Hz平移動畫進(jìn)行說明:

CABasicAnimation*anim=[CABasicAnimationanimationWithKeyPath:@"transform.translation.y"];CGFloatspeed=170.0/330.0;anim.toValue=@(100);anim.fromValue=@(0);anim.duration=10.0;anim.repeatCount=FLT_MAX;anim.preferredFrameRateRange=CAFrameRateRangeMake(120,120,120);

其中speed變量為平移的速度,單位為pt/s,試驗發(fā)現(xiàn):

speed取(0,160]時,屏幕刷新率為60Hzspeed取[161,320]時,屏幕刷新率為80Hzspeed取[321,+∞)時,屏幕刷新率為120Hz

筆者僅在iPhone13Pro上測試了平移動畫的場景,以上數(shù)據(jù)僅供參考。

最后,對于其他的常見的動畫API,例如UIView.animateWithDuration、UIViewPropertyAnimator等,則沒有提供對應(yīng)API進(jìn)行修改。理論上也可以通過某些手段拿到這些上層API所創(chuàng)建的CAAnimation對象來實現(xiàn)修改。

手勢/轉(zhuǎn)場等其他場景解鎖120Hz

其他場景需要控制動態(tài)幀率的也可以通過手動修改CADisplayLink的preferredFramePerSecond/preferredFrameRateRange屬性來實現(xiàn),其實現(xiàn)和通過監(jiān)聽RunLoop來修改滑動幀率基本相同。

UIGestureRecognizer常被用于實現(xiàn)的交互式動畫。經(jīng)過測試,發(fā)現(xiàn)在觸發(fā)手勢回調(diào)的同時啟用一個解鎖了頻率的CADisplayLink也可以間接提高UIGestureRecognizer的回調(diào)頻率,從而實現(xiàn)更高幀率的交互動畫。

對于轉(zhuǎn)場的場景,一個簡單的方案是swizzleUIViewController的生命周期消息,在出現(xiàn)/消失的節(jié)點啟用/停用CADisplayLink幀率的解鎖,從而實現(xiàn)通用的頁面轉(zhuǎn)場動畫幀率解鎖方案。

Flutter官方也計劃提供類似API讓應(yīng)用側(cè)可以針對不同的場景(滑動、動畫etc)動態(tài)切換屏幕刷新率:https://github.com/flutter/flutter/issues/90675

上線收益

基于上述思路,筆者所在團(tuán)隊在國際化短視頻業(yè)務(wù)落地了優(yōu)化項目,經(jīng)過實驗驗證:

大盤滑動幀率P50從81.57上升至112.2核心業(yè)務(wù)指標(biāo)也有一定收益結(jié)語

近年來,Apple生態(tài)中軟硬件的發(fā)展日新月異,有軟件層的dyld的持續(xù)優(yōu)化和iOS15新引入的Prewarm機制,也有新的ProMotion屏幕,可以看到Apple一直致力于打造更絲滑流暢的用戶體驗。

Apple提供的系統(tǒng)級優(yōu)化方案一般通用而無感知,但通用往往也意味著一定的局限性,可能預(yù)留了額外優(yōu)化空間,應(yīng)用開發(fā)者們可以進(jìn)一步去研究如何更好地適配。

例如本文中,筆者通過研究新引入的ProMotion屏幕背后的機制,透過表象/深入?yún)R編管中窺豹看到一部分本質(zhì),最終落地了監(jiān)控+優(yōu)化的方案,讓大盤滑動幀率P50從80上升至112左右,取得了額外的業(yè)務(wù)收益。

最后,筆者認(rèn)為,我們普通開發(fā)者作為Apple生態(tài)鏈中的一環(huán),在享受系統(tǒng)級別優(yōu)化自動帶來的收益的同時,也應(yīng)該主動去了解上述優(yōu)化背后的底層原理。一方面,了解與學(xué)習(xí)Apple的成熟優(yōu)化思路可以提升我們作為工程師的眼界。另一方面,對系統(tǒng)底層原理的了解可以拓充我們的“彈藥庫”,對業(yè)務(wù)價值交付的全鏈路了解越廣越深,越有可能抓住潛在的優(yōu)化點,從而在性能優(yōu)化工程師這條職業(yè)道路上走得更遠(yuǎn)更好。

參考資料WWDC20-10077EliminateanimationhitcheswithXCTest

https://developer.apple.com/videos/play/wwdc2020/10077

WWDC21-10147Optimizeforvariablerefreshratedisplays

https://developer.apple.com/videos/play/wwdc2021/10147/

OptimizingProMotionRefreshRatesforiPhone13ProandiPadPro

https://developer.apple.com/documentation/quartzcore/optimizing_promotion_refresh_rates_for_iphone_13_pro_and_ipad_pro?language=objc

WhatisAdaptiveSync?

https://www.viewsonic.com/library/tech/explained/what-is-adaptive-sync/

https://github.com/flutter/flutter/issues/90675加入我們

我們是字節(jié)國際化短視頻基礎(chǔ)技術(shù)團(tuán)隊,是一個深度追求極致的團(tuán)隊,我們專注于性能、架構(gòu)、包大小、穩(wěn)定性、自動化測試、基礎(chǔ)庫、編譯構(gòu)建等方向的深耕,保障超大規(guī)模團(tuán)隊的研發(fā)效率和全球數(shù)億用戶的使用體驗。目前上海、杭州、新加坡、美國都有大量人才需要,歡迎有志之士與我們共同建設(shè)億級用戶全球化APP!

可以點擊「鏈接」,進(jìn)入字節(jié)跳動招聘官網(wǎng)投遞簡歷,也可以郵件聯(lián)系:kazec.liu@bytedance.com咨詢相關(guān)信息或者直接發(fā)送簡歷內(nèi)推!

猜你喜歡

主站蜘蛛池模板: 三级毛片在线播放 | 在线观看成年人免费视频 | 日本一道免费一区二区三区 | 欧美一级毛片100部 欧美一级毛片aaaaa | 国产精品99r8免费视频2022 | 日韩一区二区三区视频 | 中文字幕亚洲 综合久久 | 99久99久6久热在线播放 | 精品欧美一区二区三区在线 | 一区二区国产精品 | 亚洲久草在线 | 国内精品久久久久影院亚洲 | 亚洲成a人v| 国内免费自拍视频 | 欧美一级大黄特黄毛片视频 | 成年女人免费视频播放成年m | 亚洲精品视频久久 | 免费高清欧美一区二区视频 | 男女午夜24式免费视频 | 毛片手机在线视频免费观看 | 欧美一级特黄刺激爽大片 | 国产亚洲福利精品一区二区 | 亚洲国产成人久久综合野外 | 91香焦视频 | 久久亚洲精品成人 | 日韩视频久久 | 欧美一级毛片生活片 | 国产在线观看一区二区三区四区 | 色婷婷国产精品欧美毛片 | 亚洲国产网站 | 大尺度福利视频奶水在线 | 九九免费精品视频在这里 | 亚洲欧美中文字幕在线网站 | 欧美第五页 | 国产一区二区在线 |播放 | 亚洲日本综合 | 亚洲综合色一区二区三区另类 | 欧美日本在线三级视频 | 成人欧美一区在线视频在线观看 | 亚洲视频在线一区二区 | 97成人在线视频 |