1. 程式人生 > >【Unity】扒一扒Profiler中這幾個“佔坑鬼”

【Unity】扒一扒Profiler中這幾個“佔坑鬼”

原文連結:http://blog.uwa4d.com/archives/presentandsync.html

WaitForTargetFPS、Gfx.WaitForPresent 和 Graphics.PresentAndSync是我們經常會被問到的引數。想必正在讀此文的你也經常在Profiler中遇到過這幾項CPU開銷過大的情況。對此,我們今天就來好好地聊一聊這幾個引數的具體含義和觸發規則。

WaitForTargetFPS
該引數一般出現在CPU開銷過低,且通過設定了目標幀率的情況下(Application.targetFrameRate)。當上一幀低於目標幀率時,將會在本幀產生一個WaitForTargetFPS的空閒等待耗時,以維持目標幀率。

解析:該項在Unity引擎的主迴圈中其實是最早執行的,即引擎實際上是根據上一幀的CPU耗時,在當前幀中通過增補WaitForTargetFPS的方式來將執行FPS維持到目標值。比如,目標幀率為30幀/秒,上一幀耗時15ms,那麼當前幀中WaitForTargetFPS將會是18(33-15)ms,但是這一幀中其他耗時為28ms,那麼在Profiler中這一幀的總耗時就變成了46(18+28)ms。

因此,由該值造成了Profiler開銷較高的現象,其實是耗時的“假象”,在優化過程中,你對它可以“視而不見”。

Gfx.WaitForPresent && Graphics.PresentAndSync
這兩個引數在Profiler中經常出現CPU佔用較高的情況,且僅在釋出版本中可以看到。究其原因,其實是CPU和GPU之間的垂直同步(VSync)導致的,之所以會有兩種引數,主要是與專案是否開啟多執行緒渲染有關。當專案開啟多執行緒渲染時,你看到的則是Gfx.WaitForPresent;當專案未開啟多執行緒渲染時,看到的則是Graphics.PresentAndSync。

Graphics.PresentAndSync 是指主執行緒進行Present時的等待時間和等待垂直同步的時間。Gfx.WaitForPresent其字面意思同樣也是進行Present時需要等待的時間,但這裡其實省略了很多的內容。其真實的意思應該是為了在渲染子執行緒(Rendering Thread)中進行Present,當前主執行緒(MainThread)需要等待的時間。聽起來依然很拗口,下面,我們就來進行詳細地解釋。

當專案開啟多執行緒程渲染時,引擎會將Present等相關工作儘可能放到渲染執行緒去執行,即主執行緒只需通過指令呼叫渲染執行緒,並讓其進行Present,從而來降低主執行緒的壓力。但是,當CPU希望進行Present操作時,其需要等待GPU完成上一次的渲染。如果GPU渲染開銷很大,則CPU的Present操作將一直處於等待操作,其等待時間,即為當前幀的Gfx.WaitForPresent時間,如下圖所示。

同理,當專案未開啟多執行緒渲染時,引擎會在主執行緒中進行Present(當前絕大多數的移動遊戲均在使用該中操作),當然,Present操作同樣需要等待GPU完成上一次的渲染。如果GPU渲染開銷很大,則CPU的Present操作將一直處於等待操作,其等待時間,即為當前幀的Graphics.PresentAndSync時間,如下圖所示。

我們做了一個較為極端的例子來展示這種情況。在Unity 5.3.3版本上,建立60個全屏UIPanel,分別開啟和關閉多執行緒渲染,並不設定TargetFPS。那麼,在三星S6裝置上該引數的CPU開銷如下:

開啟多執行緒渲染時:

關閉多執行緒渲染時:

所以,如果你的專案中,Gfx.WaitForPresent或Graphics.PresentAndSync的CPU耗時非常高時,其實並不是它們自己做了什麼神祕的操作,而是你當前的渲染任務太重,GPU負載過高所致。

同時,對於開啟垂直同步的專案而言,Gfx.WaitForPresent 和 Graphics.PresentAndSync也會出現CPU佔用較高的情況。在解釋這種問題之前,我們先以“大家乘坐地鐵”來舉個例子。一般來說,地鐵到達每一站的時間均是平均且一定的,假設每10分鐘一班接走一批乘客。但是幾乎沒有多少乘客可以按點到達,如果提前兩分鐘到達,則只需要等待兩分鐘即可乘上地鐵,但是,如果你錯過了,哪怕只差了一分鐘,那麼你也不得不再等待九分鐘才能乘上地鐵。

上述的情況我們經常會遇到。在GPU的渲染流水線中,其轉換front buffer和back buffer的工作原理和“乘坐地鐵”其實是一致的。大家可以把GPU的流水線簡單地想象成為一列地鐵。對於移動裝置來說,GPU的幀率一般為30幀/秒或60幀/秒,即VSync每33ms或每16.6ms“到站一次”,CPU的Present即為“乘客乘上地鐵”,然後前往各自的目的地。與乘客的早到和晚到一樣,CPU的Present也會出現類似的情況,比如:

CPU端開銷非常小,Present在很早即被執行,但此時VSync還沒到,則會出現較高的等待時間,即Gfx.WaitForPresent 和 Graphics.PresentAndSync的CPU開銷看上去很高。下圖為Unity 5.3.3版本上,一個空場景在不開啟多執行緒渲染、不設定TargetFPS的情況下,Graphics.PresentAndSync在三星S6裝置上的CPU佔用情況。

CPU端開銷很高,使得Present執行時錯過了VSync操作,這樣,Present將不得不等待下一次VSync的到來,從而造成了Gfx.WaitForPresent 和 Graphics.PresentAndSync的CPU開銷較高。這種情況在CPU端載入過量資源時特別容易發生,比如WWW載入較大的AssetBundle、Resource.Load載入大量的Texture等等。

通過以上的講解,我們希望此刻的你已經對Gfx.WaitForPresent 和 Graphics.PresentAndSync已經有了深入的理解。這兩個引數無論CPU佔用多少,其實都不是這兩個引數的自身問題,而是專案的其他部分造成。對此,我們做一個總結,以方便你進一步加深印象。

造成這兩個引數的CPU佔用較高的原因主要有以下三種原因:

CPU開銷非常低,所以CPU在等待GPU完成渲染工作或等待VSync的到來;
CPU開銷很高,使Present錯過了當前幀的VSync,即不得不等待下一次VSync的到來;
GPU開銷很高,CPU的Present需要等待GPU上一幀渲染工作的完成。

最後,如何優化並降低這兩個引數的CPU佔用呢? 那就是,忽略Gfx.WaitForPresent 和 Graphics.PresentAndSync這兩個引數,優化其他你能優化的一切!
--------------------- 
作者:UWA 
來源:CSDN 
原文:https://blog.csdn.net/UWA4D/article/details/53709413 
版權宣告:本文為博主原創文章,轉載請附上博文連結!