close

點擊關注「有贊coder」

獲取更多技術乾貨哦~


作者:米廣

部門:業務技術/iOS組


前言


WWDC21中發布的macOS Monterey中新增了可變刷新率的Adaptive-Sync顯示技術,自此行業通用的可變幀率技術登錄Mac生態;今天我們就圍繞蘋果生態中的兩種可變幀率顯示技術,討論如何為用戶呈現最佳體驗;本文中首先我們會介紹一下macOS中的Adaptive-Sync技術;這項技術為macOS的全屏顯示的App和遊戲提供了更加靈活的幀率,更加流暢體驗,基於此深入討論有關順滑渲染的最佳實踐;然後我們會了解現有的iPad Pro和iPhone 13 Pro上的ProMotion技術,並進一步探討能在不同幀率下基於CADisplayLink的最佳技術實踐,在自定義繪圖時為用戶帶來流暢的體驗;本篇文章是基於Session10147 - Symbolication: Beyond the basics撰寫,該Session的演講者是來自Apple GPU軟件團隊的WindowServer工程師Kyle Sanner和CoreAnimation工程師 Alex Li。

1

基於macOS 平台中的Adaptive-Sync技術為用戶提供順滑體驗


Adaptive-Sync 技術簡介

Adaptive-Sync是由美國的非營利性標準制定組織VESA提出的基於DisplayPort接口的可變幀率顯示技術,目的是為了解決畫面撕裂和卡頓掉幀的問題;我們平時常用的DisplayPort接口就是VESA制定的標準接口;該組織不同於索尼等盈利性商業公司, DisplayPort相較於索尼的HDMI是一種免費而開放的技術,而HDMI受專利保護且是盈利性收費視頻傳輸界面,當然HDMI有完善的數字內容保護體系HDCP;Adaptive-Sync類似於現AMD的Free-Sync和NVIDIA的G-Sync的動態幀率技術,但該技術免費且開放;因此在不同品牌的顯示器和不同品牌的顯卡中,只要都支持Adaptive-Sync,那就支持動態幀率技術,不受制於AMD顯卡必須買AMD認證支持的Free-Sync顯示器等問題;免費開放技術將利於市場廣泛推廣應用可變幀速率顯示生態;蘋果已經明確在macOS中將提供基於Adaptive-Syn的可變幀率顯示技術支持。

首先,我們來回顧一下 Apple 平台中的屏幕類型~

Apple 生態中的大部分顯示器都是固定幀率的,也就是屏幕只要被點亮,就會以每秒固定的刷新頻率進行刷新與顯示;但iPad Pro和最新發布的iPhone13Pro系列的ProMotion和最近發布的Mac和MacBook Pro 2021中的Adaptive-Sync和ProMotion是個例外;首先,我們來了解一下Mac中的Adaptive-Sync新技術。

固定與可變幀率的區別

在講解可變幀率的屏幕刷新技術前,我們先回顧一下固定幀率的顯示技術;如下圖所示,在60Hz的顯示器中,幀與幀間的刷新間隔是固定的16毫秒;如果在幀緩存流里準備好了新的一幀,新的一幀就會被呈現出來;如果沒有準備好新的幀,那麼前一幀就會被繼續顯示;當固定幀率提高到120Hz時,我們提高了一倍幀刷新率,這導致每一幀的準備時間縮小了一倍到8毫秒;但固定幀率的顯示是相似的,只是刷新速度的快慢有區別,也就是幀準備時間長短有別。

Adaptive-Sync 可變幀率帶來的變化和優勢

在Adaptive-Sync顯示中,每一幀都有一個可變的時間窗口,這個時間窗口替代了原有的固定的幀刷新時間間隔;這個間隔取決於具體連接的可變幀率顯示器的幀率支持範圍;下面以可變幀率40-120Hz為例,這意味着每一幀可以在屏幕中展示8-25毫秒;但需注意,一旦一個幀的展示時間超過了最大的25毫秒的極值,系統就會強制刷新幀,刷新期間會有短暫的不可用時間。

我們對比一下,就可以發現可變幀率帶來的好處;在120Hz固定幀率的屏幕中,如果App能夠在8毫秒內完成幀繪製,這將給用戶帶來一個順滑的120Hz體驗,但假設由於場景複雜度提升,某些幀的繪製時間超過了8毫秒,需要9毫秒才能在幀緩存中準備完畢,這會導致前一幀顯示16毫秒(重新被展示一次),而不是8毫秒;這種實際耗時16毫秒的幀,會導致呈現給用戶的實際顯示效果,有明顯可察覺的卡頓。

在可變幀率的顯示器中,您可以設置幀在繪製完成後立刻呈現至屏幕,而無需在固定時間節點提交呈現幀;因此如果當前幀的繪製用時為9毫秒,那麼在繪製完成時就可以主動提交幀顯示,這其中1毫秒的延遲,不會導致易被用戶察覺的卡頓;在這種工作模式下,針對複雜幀的繪製,可以通過拆分複雜幀繪製任務,以及適當增加幀繪製時間,來提供一個平滑、均勻的幀繪製。

基於真實場景的 Adaptive-Sync 的最佳實踐

需要可變幀率的場景

設想這種情況:一個可能運行複雜場景的遊戲,基本可以穩定在90Hz的刷新速率,但特定複雜場景會導致幀速率下降至66Hz;通過實時監測GPU的工作負荷,開發者可以有意在複雜場景中降低畫面質量或適當增加幀繪製時間,直至畫面場景複雜度恢復至平均水平;如此操作,可以為用戶提供一種較為順滑的幀呈現。

基於此,我們可以發現固定幀率和動態幀率的最佳實踐的不同;在固定幀率的機制中,如果幀繪製時間超過現有顯示器幀率的固定時間時,我們會建議將所有幀繪製的時間都延長,也就是使用更低的陣刷新速率,以使所有幀繪製都能夠在刷新間隔中在GPU上完成;下圖就展示了在一個固定60Hz幀率顯示器中,42Hz刷新率時部分幀會無法呈現或被跳過,因此只能降級到30Hz進行幀繪製。

而在可變刷新幀率機制中,我們會建議App在任何情況下都應該盡力提供更高的幀刷新速率,App需要平衡GPU負載和刷新率之間的平衡,最大的幀渲染時長不能超過最低動態幀率的間隔,否則會導致劇烈可察覺的卡頓。因此,您無需擔心現有顯示器所支持的可用幀率是固定的組合,您可以竭盡所能提交順滑繪製,也就是儘量均等的安排幀繪製耗時,而動態調整輸出的幀步調(幀率),在Adaptive-Sync可變幀率環境中,在可支持範圍內的幀率都可以被正常呈現,如48Hz與110Hz。

啟用 Adaptive-Sync

基於此,我相信您已經對可變幀率有了進一步的理解;我們來談談如何在遊戲中啟用Adaptive-Sync可變幀刷新率技術。

首先需要軟硬件的支持,包括M1架構的Mac和近年來部分Intel架構的Mac;其次需要Adaptive-Sync支持的顯示器,並開啟顯示器的Adaptive-Sync功能;顯示器與Mac通過 DisplayPort界面連接;其次遊戲和App必須以全屏方式運行。

接下來我們深入API來進一步了解Adaptive-Sync首先您需要獲取當前環境是否支持可變刷新幀率,對此您可以通過 NSScreen的新屬性來判斷;在支持可變刷新幀率的環境中,這兩個值會反應最大和最小幀率所對應的刷新時間間隔;而在不支持可變幀率的環境中,這兩個值會是相等的數值;同時需要判斷當前App是否在全屏模式中運行;最後通過上述兩個條件,確保Adaptive-Sync已經正常開啟。

在繪製中控制幀步調

基於Metal繪製技術提供的API,動態調整幀繪製的步調,以在自適應同步顯示器上流暢顯示。您可以直接使用我們的MetalDrawable相關API ,這些API已經內置了幀步調技,例如presentAfterMinimumDuration或者 presentAtTime;當然您也可以基於present now邏輯自己實現呈現邏輯和自定義計時器管理。

我們來結合一個簡單的例子來看看與之前固定幀率顯示器相比,我們需要為App做出哪些調整;在這個例子中,我們會獲取一個Drawable實例,設置好GPU的工作,完成後呈現在屏幕上;我們需要依賴於GPU完成接下來 Drawable渲染工作的壓力來設置幀速率。在固定幀率顯示器上,我們很容易知道這不是最好的實踐邏輯,因為我們無法保證GPU的渲染頻率與顯示器幀速率保持一致;但基於Adaptive-Sync顯示器和動態幀步調,同樣是之前這個場景,我們可以在Instruments中觀察到個別幀消耗時間有所增加,這似乎沒有問題;但這個例子中的實際問題是,特定場景的單獨幀,會周期的被顯示,這個複雜場景幀會消耗更多的時間,會形成用戶可能感知的卡頓,在下圖中紅色部分所示:

首先讓我們嘗試以較大的固定平均的渲染速率來解決這個問題;當然在用戶可以自定義幀率的情形中,也可以使用該方法;我們嘗試固定幀率為78Hz,並將該間隔值傳入afterMinimumDuration中。

實際效果我們將得到一個幀率低但渲染均勻順滑的效果,同時整個App占用了更少的CPU和GPU資源消耗;這種方法可以解決用戶遇到的卡頓,但實際會導致整個App的體驗降低(由於強制降低了整體的幀率)。

基於 GPU 負荷動態平滑實際繪製幀率

而在Adaptive-Sync顯示器中,我們可以嘗試通過GPU負載(前述幀渲染所需時間)動態計算,而非直接賦予固定值的方式來設定幀速率;具體算法如下圖所示,我們通過計算前述幀的滾動平均值,來動態預估下一幀所用時間,結合Adaptive-Sync ,動態賦予下一幀所需預估的時間;這裡的初始值設為顯示器最高幀速率時的可用繪製時間。

最終我們可以充分利用GPU資源為用戶呈現儘可能高幀率的畫面;且在不同性能設備,不同的GPU負載下,都無需進行額外的代碼更改,如下圖所示,性能優秀與過時的Mac都有一個均勻的幀呈現速率。

綜上,我們了解了幾個新API,以及根據GPU負載的動態幀速率控制思路,讓我們能夠利用Adaptive-Sync技術為用戶提供更順滑的渲染體驗;這篇文章編寫時,還未發布MacBook Pro 2021,目前MacBook Pro 2021的內建屏幕支持了ProMotion顯示,實際上外接顯示器和內部ProMotion顯示器的驅動方法有所區別,具體區別蘋果官方目前也未說明。目前除了預覽版Safari支持120Hz,其他任何Mac App都暫時不能以120Hz驅動這塊內建屏幕,有關更多信息可以關注蘋果的API與進一步文檔更新,接下來讓我們繼續了解一下iPad與iPhone 13 Pro中的可變刷新率技術。

2

基於 iPad Pro 與 iPhone 13 Pro 平台中的 ProMotion 技術為用戶提供順滑顯示體驗

ProMotion 可變幀率技術簡介

接下來我們來討論iPad Pro與iPhone 13 Pro中的ProMotion可變幀率技術;ProMotion自iPad Pro 2017年發布以來,可以提供24Hz - 120Hz的刷新率;最近發布的iPhone 13 Pro可提供10Hz - 120Hz的刷新率,iPadOS 15和iOS 15中,省電模式會將ProMotion刷新率降低至60Hz ,也就是120Hz的刷新率並不總是可用;因此作為開發者,需要處理和協調幀的繪製步調以在ProMotion因種種原因而幀率下降時,仍為用戶提供正確流暢的渲染內容,接下來我們將討論 。

正如之前提到的60Hz的顯示器16毫秒刷新一次,保持固定的刷新節奏,當屏幕限制30Hz、20Hz的內容是,顯示器本身仍舊保持60Hz的刷新率,因此相同幀會被重複展示,這種不可察覺的刷新操作會影響電池使用時長。

而在ProMotion技術顯示器上,幀刷新速率最高為120Hz ,iPad Pro最低24Hz iPhone 13 Pro最低為10Hz,ProMotion在不同刷新頻率下,不會重複刷新之前幀,而是根據當下的幀速率動態刷新幀,因此刷新間隔從8毫秒到99毫秒不等;動態的幀率刷新可以節約電池使用時長;請注意ProMotion顯示器與Adaptive-Sync顯示器有所區別,ProMotion顯示器無法支持基於區間的可變幀速率,而Adaptive-Sync顯示器支持,例如您可以在Adaptive-Sync 中以110Hz、78Hz、49Hz任意在支持區間內的幀速率來呈現幀,但在ProMotion中只有特定的120Hz、60Hz、40Hz、24Hz、10Hz等特定支持的幀率,後文中將會提到這點。

ProMotion `120Hz` 的可用性限制

ProMotion 的120Hz並不總是可用幀速率,用戶可以在輔助功能設置中打開限制幀速率動態變換,將最大幀率限制至60Hz;當設備負載過大,出現過熱情況時,系統會限制120Hz的可用性;在iPadOS 15和iOS 15中,低電量模式的的設備會強制限制ProMotion最大幀率為60Hz。對於上述情況,絕大多數App都無需特別適配。但如果您的App執行逐幀的自定義繪製,那麼您需要處理上述幀速率限制事件。

ProMotion 繪製的最佳實踐

CADisplayLink 與動態幀率

首先DisplayLink有基於CoreAnimation的CADisplayLink和基於CoreVideo的CVDisplayLink,前者在除macOS之外的系統中可用,後者在macOS中可用;因為iPad與iPhone 13 Pro中的ProMotion基於iPadOS和iOS ,因而這裡我們只討論CADisplayLink;DisplayLink的vsync callback事件可以理解為與屏幕幀刷新速率穩定同步的一個計時器回調;在設備的幀速率發生變化時,與CADisplayLink的時序回調保持一致的幀渲染步調,是保證App順滑體驗的關鍵;雖然DisplayLink可以視為一個計時器,但我們不能自己新建NSTimer計時器來實現此邏輯,因為自己新建的NSTimer的步調不可能和可能變化的幀速率同步;通過給CADisplayLink的preferredFramesPerSecond賦值,可以調節vsync的回調間隔貼近於您所期望的值;也可以通過timestamp和targetTimestamp來獲取上一幀和下一幀的渲染時長等上下文信息,必要時可以像前述提到的Adaptive-Sync的動態幀率計算邏輯,來實現一個基於當前環境的最大幀率;請注意在iOS設備中,只有自定義的CALayer渲染內容,以及Metal API的內容需要自己控制ProMotion的顯示幀率,其他框架和 PI目前蘋果已經完成內部實現更新,自動支持ProMotion, 具體支持情況如下:

繪製步調的最佳實踐

在這裡我們並不直接討論如何實現自定義動畫和渲染循環,但我們提供4個最佳實踐,幫助您自定義繪圖步調儘量與vsync回調的時間保持一致,以及避免一些常犯的錯誤。

1、獲取當前硬件幀速率等信息

您可以從UIScreen獲取硬件支持最大幀速率,在ProMotion顯示器中,這個值永遠為120Hz,即使有前述的降低幀率的事件發生,這裡仍然為。20Hz。

2、獲取CADisplayLink的實際幀速率

請注意在ProMotion中,您可以通過給setPreferredFramesPerSecond賦值,來聲明您想要的幀率,但由於ProMotion支持的幀率是有限的120Hz、60Hz、40Hz、30Hz、24Hz、10Hz(10Hz只有iPhone支持),因此申請在這些既定幀率之外的幀率(如68Hz),系統會自動選擇一個與您聲明幀率就近的支持的幀率來顯示60Hz);具體真實幀率邏輯會類似下圖中的場景。

3、 使用targetTimestamp來規劃繪圖所用時間

下面的例子中, 當CADisplayLink的實際幀速率發生變化時,targetTimeStamp相較於timestamp屬性與實際變化更同步,基於此步調渲染和提交顯示的幀,更為順滑,而不會有明顯可感知的卡頓。

因此,在ProMotion中,儘量使用targetTimestamp而不是timestamp來規劃幀繪製時間和提交節奏;在實際使用中,您可以使用targetTimestamp屬性直接替換現有代碼中所有的timestamp屬性;就是這麼簡單。

4、動態計算合適的幀速率

targetTimestamp與imestamp之間的差值反映了預估的vsync callback之間的時間,但實際時間可能有所差異;比如CPU和GPU被其他高優先級任務所調度,或是runloop在忙於一些其他事,這些情況下,vsync callback回調可能被完全略過(回調丟失);因此保持正確的自定義繪圖步調是提供順滑用戶體驗的關鍵。

下面的例子包含了CADisplayLink回調延時與回調跳過兩種情況

一般而言針對回調延時,會採取捨棄一幀的策略;回調跳過發生時,則一般採取捨棄一幀並提前繪製下一幀的策略;現在假設這一幀的繪製工作花了太長時間,下一次回調且需等待runloop釋放,因為這次回調被延遲了,那下一次回調將被直接跳過;這種情況下,如果計劃提前開始繪製下一幀時,需要注意這裡的可用時間是16毫秒,而非正常的8毫秒;為了追蹤到這個時間差,可以記錄上一次targetTimestamp與本次targetTimestamp來準確獲得這個時間。

因此基於targetTimestamp和timestamp差值來判斷當前是否回調被跳過,進而提前渲染之後幀的操作,會導致每次回調被丟失時,您自定義提前繪圖也減慢了一幀;而追蹤targetTimestamp和 上一次targetTimestamp之間的差值,來保證獲取正確的剩餘時間,進而可以在回調被跳過時,正確提前繪製下一幀;當然如果您的繪製任務很大,建議基於targetTimestamp提供的值來動態調整繪製工作量,以達成這裡的繪製時間要求。

總結上述 ProMotion 最佳實踐

回顧本Session 10147,我們先討論了macOS中的Adaptive-Sync動態幀速率技術,以及如何基於此技術為用戶提供更加順滑的渲染效果體驗;之後,我們討論了如何在 iPad Pro 和 iPhone 13 Pro設備中基於ProMotion技術的CADisplayLink最佳實踐,請注意這兩種顯示技術之間的區別,以及最佳實踐的不同;隨着顯示技術的不斷發展,我們希望本篇文章為您在日益動態的顯示時序技術應用中提供一些幫助;

在此向您推薦WWDC17 - Introducing Metal 2和 WWDC19 - Delivering Optimized Metal Apps And Games。






12月18日有贊移動團隊和愛逛移動團隊合辦第二屆「移動技術沙龍」,期待在現場和大家一起探討移動應用的質量與性能提升之路~


‍‍
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()