CPU bound:CPU性能邊界,是指CPU計算時一直處於佔用率很高的狀況。
GPU bound:GPU性能邊界,一樣的是指GPU計算時一直處於佔用率很高的狀況。
簡介
在這篇文章中,咱們將學習Unity渲染一幀時在幕後會發生什麼,渲染時會出現什麼樣的性能問題,以及如何修復與渲染相關的性能問題。
在閱讀本文以前,有一點須要記住的是,即沒有適合全部狀況的方法能夠提升渲染性能。渲染性能受到遊戲中許多因素的影響,同時也高度依賴於遊戲所運行的硬件和操做系統。最重要的是要記住,咱們經過研究、實驗和嚴格分析實驗結果來解決性能問題。
這篇文章包含了關於渲染的最多見的性能問題的信息,以及如何修復這些問題的建議和進一步的延伸閱讀連接。咱們的遊戲可能會有一個或多個問題這裏並無提到。然而,這篇文章仍然會幫助咱們理解咱們的問題,並給咱們知識和詞彙來有效地尋找解決方案。
渲染的簡要介紹
在咱們開始以前,讓咱們來看看Unity渲染一幀時會發生什麼。理解事件流和正確的術語將幫助咱們理解、研究和解決性能問題。
注:在本文中,咱們將使用術語「object」來表示遊戲中可能渲染的對象。任何帶有Renderer組件的GameObject都會被稱爲object。
在最基本的層面上,渲染能夠描述以下:
- 中央處理器,即CPU,計算出必須繪製什麼以及如何繪製。
- CPU向圖形處理單元(即GPU)發送指令。
- GPU根據CPU的指令繪製物體。
如今讓咱們仔細看看發生了什麼。在本文後面的部分中,咱們將更詳細地介紹這些步驟,可是如今讓咱們先熟悉所使用的單詞,並理解CPU和GPU在渲染過程當中扮演的不一樣角色。
描述渲染經常使用的短語是渲染管線,這是一個須要牢記的過程;高效的渲染就是保持數據高效地流動。
對於渲染的每一幀,CPU都作了如下工做:
- CPU檢查場景中的每一個對象,已肯定是否應該渲染它。對象只有在知足某些條件時才被渲染;例如,它的包圍盒的某些部分必須位於攝像機的視錐體內。將不渲染的對象稱爲剔除對象。有關視錐體和視錐體剔除的更多信息,請參見此頁。
- CPU收集關於將要渲染的每一個對象的信息,並將這些數據排序爲稱爲draw call的命令。一個draw call包含關於單個網格的數據以及如何渲染該網格;例如,應該使用哪些紋理。在某些狀況下,共享設置的對象能夠組合到同一個draw call中。將不一樣object的數據組合到同一個draw call中稱爲批處理。
- CPU爲每一個draw call建立一個稱爲批處理的數據包。批處理有時可能包含draw call調用以外的數據,可是這些狀況不太可能致使常見的性能問題,所以咱們在本文中不考慮這些狀況。
對於每一個包含一個draw call的批處理,CPU如今必須執行如下操做:
- CPU能夠向GPU發送一個命令來更改一些變量,這些變量統稱爲渲染狀態。這個命令稱爲SetPass call。SetPass call告訴GPU使用哪些設置渲染下一個網格。只有當渲染的下一個網格須要從前一個網格更改渲染狀態時,纔會發送SetPass call。
- CPU將draw call發送到GPU。draw call指示GPU使用最近SetPass調用中定義的設置來渲染指定的網格。
- 在某些狀況下,批處理可能須要不僅一個pass。pass是着色器代碼的一部分,新的pass須要對渲染狀態進行更改。對於批處理中的每個pass,CPU必須發送一個新的SetPass call,而後必須再次發送draw call。
同時,GPU還作了如下工做:
- GPU按照從CPU發送的順序處理任務。
- 若是當前任務是SetPass call,GPU將更新渲染狀態。
- 若是當前任務是一個draw call,GPU渲染網格。這是分階段進行的,由着色器代碼的不一樣部分定義。這部分渲染是很複雜的,咱們不會詳細討論它,可是理解一段叫作頂點着色器(vertex shader)的代碼是頗有幫助的。頂點着色器告訴GPU如何處理網格的頂點,而後一段稱爲片斷着色器(fragment shader)告訴GPU如何畫出每一個像素。
- 這個過程重複進行,直到全部從CPU發送的任務都被GPU處理。
如今咱們已經瞭解了 Unity渲染一幀時所發生的事情,讓咱們考慮一下渲染時可能出現的的問題。
渲染問題的類型
關於渲染最重要的一點是:爲了渲染一幀,CPU和GPU都必須完成全部的任務。若是這些任務中的任何一個花費太長時間來完成,它將致使幀的渲染延遲。
渲染的問題有兩個基本緣由。第一類問題是由低效的管線引發的。當渲染管線中的一個或多個步驟花費太長時間沒法完成時,就會出現低效率的管線,從而中斷數據的順暢流動。管線內的低效被稱爲瓶頸(bottlenecks)。第二種類型的問題是因爲試圖經過向管線推送太多的數據而致使的。即便是最有效的管線也有一個限制,即在一幀中能夠處理多少數據。
當咱們的遊戲是由於CPU花費太多的時間去執行任務而致使須要花費很長的時間渲染一幀時,咱們的遊戲就是所謂的CPU邊界(CPU bound)。當咱們的遊戲由於GPU執行它的渲染任務而花費太多的時間渲染一幀時,咱們的遊戲就是所謂的GPU邊界(GPU bound)。
理解渲染問題
在進行任何修改以前,使用Profiler分析工具來了解性能問題的緣由是很是重要的。不一樣的問題須要不一樣的解決方案。一樣重要的是,咱們要衡量咱們所做的每個修改的效果;解決性能問題是一種平衡的行爲,改進性能的一個方面可能會對另外一個方面產生負面影響。
咱們將使用兩個工具來幫助咱們理解和修復渲染問題:Profiler和Frame Debugger。這兩個工具都內置在Unity中。
Profiler Window
Profiler窗口容許咱們查看關於遊戲執行狀況的實時數據。咱們可使用Profiler查看關於遊戲的許多方面的數據,包括內存使用、渲染管線和用戶腳本的性能。
Frame Debugger
幀調試器容許咱們一步步地查看幀是如何渲染的。使用幀調試器,咱們能夠看到詳細的信息,例如每次draw call中繪製的內容、每次draw call的着色器屬性以及發送到GPU的事件順序。這些信息幫助咱們理解遊戲是如何渲染的,以及咱們能夠在哪裏提升性能。
若是你還不熟悉幀調試器的使用,Unity手冊中
關於幀調試器的部分是一個很是有用的指南。
發現形成性能問題的緣由
在咱們嘗試提升遊戲的渲染性能以前,咱們必須確保遊戲是因爲渲染問題而運行緩慢的。若是問題的真正緣由是過於複雜的用戶腳本,那麼嘗試優化渲染問題是沒有意義的。若是你不肯定你的性能問題是否與渲染有關,請查看這一篇
教程。
一旦咱們肯定了咱們的問題與渲染有關,咱們還必須瞭解咱們的遊戲是CPU bound,仍是GPU bound。這些不一樣的問題須要不一樣的答案,因此在嘗試解決問題以前,咱們必須瞭解問題的緣由,這一點很是重要。若是你還不肯定你的遊戲是CPU bound仍是GPU bound,你應該查看
這一個教程。
若是咱們肯定咱們的問題與渲染有關,而且咱們知道咱們的遊戲是CPU bound仍是GPU bound,咱們就能夠繼續下面的內容了。
若是咱們的遊戲是受到了CPU的限制
通常來講,CPU渲染一幀所必須完成的工做分爲三類:
- 決定繪製什麼
- 爲GPU準備命令
- 向GPU發送命令
這幾個類包含許多單獨的任務,這些任務能夠跨多個線程執行。線程容許獨立的任務同時發生;當一個線程執行一個任務時,另外一個線程能夠執行一個徹底獨立的任務。這意味着工做能夠作得更快。當渲染任務被分割到不一樣的線程時,這稱爲多線程渲染。
在Unity的渲染過程當中有三種類型的線程:主線程,渲染線程和工做線程。主線程是咱們遊戲的大部分CPU任務發生的地方,包含一些渲染任務。渲染線程是一個專門的線程,它向GPU發送命令。每一個工做線程執行單個任務,例如剔除或者網格蒙皮。哪一個線程執行哪些任務取決於遊戲的設置和遊戲運行的硬件。例如,目標硬件的CPU內核越多,能分配的工做線程就越多。所以,在目標硬件上描述咱們的遊戲是很是重要的;咱們的遊戲在不一樣設備上的表現可能會很是不一樣。
由於多線程渲染是複雜的,而且依賴於硬件,因此在咱們嘗試提升性能以前,咱們必須瞭解是哪些任務致使咱們的遊戲受到了CPU的限制。若是咱們的遊戲運行緩慢是由於在一個線程上的剔除操做花費太多的時間,那麼它將沒法幫助咱們減小在另外一個線程上向GPU發送命令所花費的時間。
注意:並不是全部平臺都支持多線程渲染;在撰寫本文時,WebGL不支持這個特性。在不支持多線程渲染的平臺上,全部CPU任務都在同一個線程上執行。若是咱們的CPU限制出如今這樣的平臺上,那麼優化任何CPU的工做都將提升CPU性能。若是這是咱們遊戲的狀況,咱們應該閱讀一下全部部分並考慮哪些優化可能最適合咱們的遊戲。
圖形工做
在Player Settings中的
Graphics jobs 選項決定了Unity是否使用工做線程來執行渲染任務,而這些任務在其餘狀況下是在主線程上完成的。在有此功能的平臺上,它能夠提供至關大的性能提高。若是咱們但願使用這個特性,咱們應該在啓用和不啓用Graphics jobs的狀況下對遊戲進行概要分析,並觀察它對性能的影響。
找出哪些任務致使了性能問題
咱們能夠經過使用Profiler來肯定哪些任務致使咱們的遊戲受到CPU的限制。本教程展現瞭如何肯定問題所在。
如今咱們已經瞭解了哪些任務致使咱們的遊戲受到CPU的限制,讓咱們來看看一些常見的問題及其解決方案。
向GPU發送命令
向GPU發送命令所花費的時間是遊戲受到CPU限制的最多見緣由。這個任務是在大多數平臺上的渲染線程上執行的,儘管在某些平臺上(例如paystation 4)這多是由工做線程執行的。
向GPU發送命令時發生的代價最大的操做是SetPass call 。若是咱們的遊戲因爲向GPU發送命令而受到CPU限制,那麼減小SetPass call的數量多是提升性能的最佳方法。
咱們能夠在Unity的Profiler Window中的Rendering profiler區域看到發送了多少SetPass call和批處理(batches )。在性能受到影響以前能夠發送的SetPass call的數量在很大程度上取決於目標硬件;在性能受到影響以前,高端PC能夠比移動設備發送更多的SetPass calls。
SetPass calls的數量及其與批處理數量的關係取決於幾個因素,咱們將在本文後面更詳細地討論這些主題。然而,一般狀況是:
- 在大多數狀況下,減小批處理和/或讓更多的對象共用相同的渲染狀態設置將減小SetPass call的數量。
- 在大多數狀況下,減小SetPass call的數量將提升CPU性能。
若是減小批處理的數量並不能減小SetPass call的數量,那麼它自己仍然能夠提升性能。這是由於CPU處理單個批處理比處理多個批處理更有效率,即便它們包含相同數量的網格數據。
總的來講,有三種方法能夠減小批處理和SetPass call的數量。咱們將更深刻地研究這些問題:
- 減小要渲染對象的數量可能會同時減小批處理和SetPass call。
- 減小每一個對象必須渲染的次數一般會減小SetPass call的數量。
- 將必須渲染的更少批處理對象的數據組合起來將減小批處理的數量。
不一樣的技術將適用於不一樣的遊戲,因此咱們應該考慮多有這些選項,決定哪些能夠適合應用於咱們的遊戲和試驗中。
減小渲染對象的數量
減小必須渲染的對象數量是減小批處理和SetPass call數量的最簡單方法。咱們可使用幾種技術來減小渲染對象的數量。
- 簡單地減小場景中可見對象的數量是一個有效的解決方案。例如,若是咱們在人羣中渲染大量不一樣的角色,咱們能夠簡單地在場景中減小這些角色。若是場景看起來仍然很好,性能獲得了改善,那麼這多是比更復雜的技術更快的解決方案。
- 咱們可使用相機的遠裁剪面屬性來減小相機的繪製距離。此屬性是攝像機再也不渲染對象的距離,若是咱們想要掩蓋遙遠物體再也不可見的事實,咱們能夠嘗試用霧來掩蓋遙遠物體的缺少。
- 對於基於距離的更細粒度的隱藏對象的方法,咱們可使用相機的Layer Cull Distance屬性爲位於不一樣層的對象提供自定義的裁剪距離。若是咱們有不少小的前景裝飾細節,這種方法是有用的;咱們能夠在比大型地形特徵更短的距離內隱藏這些細節。
- 咱們可使用一種稱爲遮擋剔除(occlusion culling)的技術來禁用被其餘對象遮擋的對象的渲染。例如,若是咱們的場景中有一個大型建築,咱們可使用遮擋剔除來禁用其背後的對象的渲染。Unity的遮擋剔除並不適用於全部場景,這可能會致使額外的CPU開銷,設置起來也可能很複雜,但它能夠大大提升某些場景的性能。這有一篇關於在Unity中採用遮擋剔除的最佳實踐。除了使用Unity的遮擋剔除,咱們還能夠經過手動去使玩家看不到的對象變得無效來實現咱們本身的遮擋剔除。例如,若是咱們的場景包含用於過場動畫的對象,可是在以前或以後不可見,咱們應該停用它們。使用咱們本身的遊戲知識老是比要求Unity動態解決問題更有效。
減小每一個對象必須渲染的次數
實時光照,陰影和反射爲遊戲添加了大量的真實性,但可能會很是耗費性能。使用這些特性會致使對象被屢次渲染,這會極大地影響性能。
這些特性的確切影響取決於咱們爲遊戲選擇的渲染路徑。渲染路徑是在繪製場景時執行計算順序的術語,在不一樣的渲染路徑之間的主要區別在於它們如何處理實時光照,陰影和反射。通常來講,若是咱們的遊戲運行在高端硬件上,而且使用了大量的實時光照、陰影和反射,那麼延遲渲染(Deferred Rendering)多是一個更好的選擇。若是咱們的遊戲運行在低端硬件上而且不使用這些特性的話,前向渲染(Forward Rendering)可能會更適合。然而,這是一個很是複雜的問題,若是咱們但願利用實時光照、陰影和高光,最好研究一下這方面的主題和作不一樣的試驗。Unity手冊的
這個頁面提供了更多關於Unity中可用的不一樣渲染路徑的信息。這篇
教程包含了關於Unity中有關燈光爲主題的有用信息。
不管渲染路徑如何選擇,實時燈光、陰影和反射的使用都會影響咱們的遊戲性能,所以瞭解如何優化它們時很是重要的。
- Unity中的動態照明是一個很是複雜的主題,深刻討論它超出了本文的範圍,可是這個教程是對這個主題的一個很好的介紹,Unity手冊的這一頁詳細介紹了常見的照明優化。
- 動態照明是昂貴的,當咱們的場景包含不移動的對象時,例如風景,咱們可使用一種稱爲烘焙的技術來預先計算場景的光照,這樣就不須要運行時計算光照計算了。這一篇教程介紹了這種技術,Unity手冊中也有對烘焙光照這部分主題的詳細介紹。
- 若是咱們但願在遊戲中使用實時陰影,這多是咱們須要提升性能的方面。Unity手冊的這一頁是一個很好的參考,能夠調整陰影屬性的質量設置,以及這些設置如何影響外觀和性能。例如,咱們可使用陰影距離屬性確保只有附近的對象投射陰影。
- 反射探針 能夠建立真實的反射,可是在批處理方面花費的成本很是高。在考慮性能的狀況下,最好將反射的使用控制在最低限度,並在使用反射的狀況下儘量地優化反射。Unity手冊的這一頁提供了一些有關優化反射探針的指導。
將對象合併成更少的批處理
當知足某些條件時,批處理能夠包含多個對象的數據。要符合批處理條件,對象必須知足:
- 共享相同的材質實例
- 有相同的材質設置(例如,紋理,着色器和着色器參數)
批處理符合條件的對象能夠提升性能,儘管與全部優化技術同樣,咱們必須仔細分析以確保批處理的成本不超過性能收益。
對於符合批處理條件的對象,有一些不一樣的技術:
- 靜態批處理是這樣一種技術,它容許Unity批處理附近不移動的符合條件的對象。能夠從靜態批處理中獲益的一個很好的例子是一堆相似的對象,例如巨石。Unity手冊的這一頁包含了在咱們的遊戲中設置靜態的說明。靜態批處理會致使更高的內存使用,因此在分析咱們的遊戲時,咱們應該牢記這個成本。
- 動態批處理是另外一種容許Unity批處理合適對象的技術,無論對象是否移動。對於使用這種技術批處理的對象有一些限制。這些限制和說明一塊兒列在Unity手冊這一頁上。動態批處理對CPU使用有影響,這可能致使它在CPU時間上的開銷大於節省的時間。在試驗這種技術時,咱們應該牢記這一成本,並謹慎使用它。
- 批處理Unity的UI元素有點複雜,由於它可能會受到UI佈局的影響。這個來自2015年Unite Bangkok的視頻很好地概述了這個主題,還有這一個教程提供了關於如何確保UI批處理按照咱們的意願工做的深刻信息。
- GPU instancing 是一種容許對大量相同對象進行高效批處理的技術。它的使用是有限的,並非全部的硬件都支持它,可是若是咱們的遊戲在屏幕上同時有許多相同的對象,咱們可能會從這項技術中收益。Unity的這一頁包含了對Unity3d中GPU實例化的介紹,詳細介紹瞭如何使用它,哪些平臺支持它,以及在什麼狀況下它可能會對咱們的遊戲有幫助。
- 紋理圖集(Texturing atlasing)是一種將多個紋理合併爲一個較大紋理的技術。它一般用於2D遊戲和UI系統,但也能夠用於3D遊戲。若是咱們在爲咱們的遊戲創造美術資源時使用這種技術,咱們就能夠確保共享紋理的對象能夠進行批處理。Unity有一個內置的紋理圖集工具,叫作Sprite Packer,用於2D遊戲。
- 能夠在Unity編輯器中或者在運行時經過代碼手動組合共享相同材質和紋理的網格。當以這種方式組合網格時,咱們必須意識到陰影、光照和剔除仍然會在每一個對象上運行。這意味着,合併網格帶來的性能提高可能會被抵消,由於不這樣作,這些對象就沒法被渲染。若是咱們但願研究這種方法,咱們應該去查看Mesh.CombineMeshes這個函數。Unity的Standard Assets資源包裏面的CombineChildren就是應用了這項技術的一個例子。
- 咱們在腳本中訪問Renderer.material時必須很是當心。這會複製一個材質而且返回新副本的引用。這樣作將打破批處理,若是渲染器再也不具備對同一材質實例的引用。若是咱們但願在腳本中訪問批處理對象的材質,咱們應該使用Renderer.sharedMaterial。
剔除、分類和批處理
對將要繪製的對象進行剔除、收集批處理數據和生成GPU命令均可能致使CPU限制(CPU bound)。這些任務能夠在主線程上執行,也能夠在單獨的工做線程上執行,這取決於遊戲的設置和目標硬件。
- 剔除(culling)自己不太多是很是昂貴的,可是減小沒必要要的剔除可能有助於性能。對於全部活動的場景對象,甚至那些沒有被渲染的圖層上的對象每一個對象都有一個相機開銷。爲了減小這種狀況,咱們應該禁用攝像機,並禁用當前未使用的渲染器。
- 批處理能夠大大提升向GPU發送命令的速度,但有時會在其餘地方增長沒必要要的開銷。若是批處理操做致使咱們的遊戲受到CPU限制,咱們可能但願限制遊戲中手動或者自動批處理操做的數量。
Skinned meshes
SkinnedMeshRenderers 是用來當咱們的網格變形動畫而使用的技術,稱爲骨骼動畫。它最經常使用於動畫角色,根據咱們遊戲的設置和目標硬件,渲染蒙皮網格的相關任務一般會在主線程或單個工做線程上執行。
渲染蒙皮網格多是一個昂貴的操做。若是咱們能在Profiler中看到渲染蒙皮網格會致使咱們的遊戲受到CPU的限制,那麼咱們能夠嘗試作一些事情來提升性能:
- 咱們應該考慮是否須要爲當前正在使用的對象添加SkinnedMeshRenderer組件。例如,咱們可能已經導入了一個使用SkinnedMeshRenderer組件的模型,但實際上咱們並無對它採用動畫。在這種狀況下,用MeshRenderer組件替換SkinnedMeshRenderer組件將有助於提升性能。在將模型導入Unity時,若是咱們選擇不在model Import Settings 中導入動畫那麼模型將會帶有一個MeshRenderer而不是SkinnedMeshRenderer組件。
- 若是咱們只是在某些時候在模型上使用動畫(例如,只有在啓動或只有當它是在相機的必定距離內),咱們能夠切換它的網格爲一個低細節版本或把MeshRender組件替換SkinnedMeshRenderer。SkinnedMeshRenderer組件有一個BakeMesh函數,能夠以匹配的姿態建立一個網格,這對於在不一樣的網格或渲染器之間進行交換很是有用,而不會對對象產生任何可見的改變。
- Unity手冊中的這一頁面包含了關於優化使用蒙皮網格的動畫角色的建議,而在Unity手冊在SkinnedMeshRenderer組件的頁面包含了能夠提升性能的調整。除了這些頁面的建議外,值得注意的是,網格蒙皮的成本增長是基於每一個頂點的;所以,在咱們的模型中使用更少的頂點能夠減小必須完成的計算量。
- 在某些平臺上,蒙皮能夠由GPU而不是CPU來處理。若是咱們在GPU上有很大的容量,這個選項可能值得一試。咱們能夠在Player Settings中爲current platform和quality target啓用GPU蒙皮。
與渲染無關的主線程操做
理解許多與渲染無關的CPU任務發生在主線程是很重要的。這意味着,若是咱們在主線程上遇到了CPU限制,那麼咱們能夠經過減小在與渲染無關的任務上花費的CPU時間來提升性能。
例如,咱們的遊戲可能在主線程上執行昂貴的渲染操做和昂貴的用戶腳本操做,使咱們的CPU達到性能邊界。若是咱們在不丟失視覺效果的狀況下儘量地優化渲染操做,咱們就有可能下降本身腳本的CPU成本,從而提升性能。
若是咱們的遊戲達到了GPU性能邊界
若是咱們的遊戲達到了GPU性能邊界,首先要作的就是找出是什麼致使GPU瓶頸。GPU的性能一般是受到填充率的限制,尤爲是在移動設備上,可是內存帶寬和頂點處理也會受到影響。讓咱們檢查一下這些問題,並瞭解致使這些問題的緣由,並學會診斷以及修復這些問題。
填充率 Fill rate
填充率是指GPU每秒能夠渲染給屏幕的像素數。若是咱們的遊戲受到填充率的限制,這意味着咱們的遊戲試圖在每一幀中繪製比GPU所能處理的更多的像素。
檢查填充率是否致使咱們的遊戲被GPU限制是很簡單的:
- 部署運行遊戲並記錄GPU時間。
- 下降Player Settings中的顯示分辨率。
- 再次部署運行遊戲。若是性能獲得改善,那麼填充率可能就是問題所在。
若是填充率是問題的緣由,有一些方法能夠幫助咱們解決這個問題:
- 片斷着色器是着色器代碼中告訴GPU如何繪製單個像素的部分。這段代碼由GPU逐像素執行的,所以若是代碼效率低下,那麼性能問題就很容易堆積起來。複雜的片斷着色器致使填充率問題的常見緣由;
- 若是咱們的遊戲使用內置的着色器,咱們應該儘量使用最簡單和最優化的着色器來得到咱們想要的視覺效果。在分組爲mobile的着色器是高度優化的;咱們應該嘗試使用它們,看看這是否可以在不影響遊戲外觀的狀況下提升性能。這些着色器是爲移動平臺設計的,可是它們適用於任何項目。在非移動平臺上使用「mobile」着色器來提升性能是徹底能夠的。若是它們提供了項目所需的視覺效果的話。
- 若是咱們遊戲中的對象使用Unity的標準着色器,理解Unity基於當前材質設置編譯這個着色器是很重要的。只編譯當前使用的特性。這意味着刪除detail maps之類的特性能夠獲得更簡單的片斷着色器代碼,從而大大提升性能。一樣,若是咱們的遊戲是這樣的狀況的,咱們應該嘗試這麼作,看看咱們是否可以在不影響視覺質量的狀況下提升性能。
- 若是咱們的項目使用定製的着色器,咱們應該儘量優化它們。優化着色器是一個複雜的主題,可是Unity手冊的這一頁和這一頁包含了優化着色器代碼的參考。
- 過分繪製(Overdraw)是指同一個像素被屢次繪製的狀況。當對象被繪製在其餘對象之上時就會發生這種狀況,這對填充率問題有很大的影響,爲了理解過分繪製,咱們必須理解Unity在場景中繪製對象的順序。一個對象的着色器決定它的繪製順序,一般是經過指定對象所在的渲染隊列。Unity使用這些信息以嚴格的順序繪製對象,詳見Unity手冊的這一頁 。此外,在繪製不一樣渲染隊列中的對象以前,對它們進行不一樣的排序。例如,Unity在Geometry隊列中對其中對象進行從前到後排序,以最小化過分繪製,但在Transparent隊列中對對象進行從後到前進行排序,以達到所需的視覺效果。這種從後到前的排序實際上能夠最大化透明隊列中對象的過分繪製。過分繪製是一個複雜的課題,沒有一個大小合適全部的方法來解決過分繪製問題,可是減小Unity不能自動排序的重疊對象的數量是關鍵。最好從Unity的場景試圖開始調查這個問題;有一個繪製模式,容許咱們看到咱們的場景過分繪製,並在那裏,肯定咱們能夠從這個地方入手來減小過分繪製。過分繪製最多見的罪魁禍首是透明材質、未優化的粒子和重疊的UI元素,因此咱們應該嘗試優化或減小這些。Unity的學習網站上這篇主要有關Unity UI優化的文章,但也包含了關於過分繪製的通常性指導。
- 使用image effects會大大增長填充率的問題,特別是若是咱們使用多個image effect。若是咱們的遊戲使用了Image effect,而且在填充率問題上遇到了困難,咱們可能但願嘗試使用不一樣的設置或image effect的更優化版本(如Bloom(Optimized)代替Boom)。若是咱們的遊戲在同一個相機上使用多個image effect,這將致使多個着色器Pass。在這種狀況下,把咱們的着色器代碼和image effect合併到一個Pass中,好比在Unity's PostProcessing Stack 。若是咱們優化了image effect,但仍然存在填充率問題,咱們可能須要考慮禁用image effect,特別是在低端設備上。
存儲帶寬
內存帶寬指的是GPU從其專用內存讀取和寫入數據的速率。若是咱們的遊戲受到內存帶寬的限制,這一般意味着咱們使用的紋理太大,GPU沒法快速處理。
要檢查內存帶寬是否有問題,咱們能夠執行如下操做:
- 配置運行遊戲並記錄GPU時間。
- 在Quality Settings中下降紋理質量。
- 再次配置運行遊戲並記錄GPU時間。若是性能獲得了改善,那麼內存帶寬可能就是問題所在了。
若是內存帶寬是咱們的問題,咱們須要在遊戲中減小紋理內存的使用。一樣,對於每一款遊戲最有效的技術也會有所不一樣,可是咱們能夠經過一些方式來優化咱們的紋理。
- 紋理壓縮是一種能夠大大減小磁盤和內存中紋理大小的技術。若是咱們的遊戲中內存帶寬是一個問題,使用紋理壓縮來減小內存中紋理的大小能夠幫助提升性能。Unity中有不少不一樣的紋理壓縮格式和設置,每一個紋理均可以有單獨的設置。通常來講,只要有可能,就應該使用某種形式的紋理壓縮;然而,嘗試爲不一樣的紋理進行不一樣的設置以達到最好的效果。Unity手冊中關於不一樣壓縮格式和設置的文章。
- Mipmaps是Unity中能夠在遠距離物體上使用的紋理的低分辨率版本。若是咱們的場景包含遠離攝像機的對象,咱們可使用mipmaps來緩解內存帶寬的問題。場景視圖中的mipmaps繪製模式容許咱們查看場景中的哪些對象能夠從mipmaps中受益,Unity手冊中的這一頁包含了更多關於爲紋理啓用Mipmaps的信息。
頂點處理
頂點處理是指GPU在網格中渲染每一個頂點時所必須作的工做。頂點處理的成本受到兩個因素影響:必須渲染的頂點數量和必須在每一個頂點上執行的操做數量。
若是咱們的遊戲達到了GPU性能邊界,而且咱們已經肯定它不受填充率或內存帶寬的限制,那麼頂點處理極可能是問題的緣由。若是是這樣,嘗試減小GPU必須處進行的頂點處理的數量可能會帶來性能的提升。
咱們能夠考慮幾種方法來幫助咱們減小頂點的數量或咱們在每一個頂點上執行的操做的數量:
- 首先,咱們應該致力於減小沒必要要的網格複雜性。若是咱們使用的網格具備在遊戲中沒法看到的細節,或者因爲建立錯誤而具備太多頂點的低效率網格,這對GPU來講是浪費工做。減小頂點處理成本的最簡單的方法是在咱們的3D建模工具中建立頂點數較低的網格。
- 咱們能夠嘗試一種叫作法線貼圖的技術,在這種技術中紋理被用來在網格上建立更復雜的幾何效果。儘管這種技術有一些GPU開銷,但在許多狀況下會帶來性能的提升。Unity手冊的這一頁介紹了使用法線貼圖來模擬複雜的幾何網格。
- 若是咱們的遊戲中的一個網格沒有使用法線貼圖,咱們一般能夠在網格導入設置中禁用該網格的頂點切線。這減小了爲每一個頂點發送到GPU的數據量。
- 細節層次(LOD),是一種優化技術,其中原理攝像機的網格減小複雜性。這在不影響遊戲視覺質量的狀況下減小了GPU渲染頂點的數量。Unity手冊中LOD頁面包含了更多關於如何在遊戲中設置LOD的信息。
- 頂點着色器是告訴GPU如何繪製每一個頂點的着色器代碼塊。若是咱們的遊戲受到頂點處理的限制,那麼減小頂着色器的複雜性可能會有所幫助。
- 若是咱們的遊戲使用內置的着色器,咱們應該儘量使用最簡單和最優化的着色器來得到咱們想要的視覺效果。例如,帶有(mobile)的着色器是高度優化了的;咱們應該嘗試使用它們,看看這是否可以在不影響遊戲外觀的狀況下提升性能。
- 若是咱們的項目使用定製的着色器,咱們應該儘量優化它們。優化着色器是一個複雜的主題,可是Unity手冊的這一頁和這一頁的着色器優化部分包含了優化着色器代碼的有用信息。
總結
咱們已經學習了Unity中渲染是如何工做的,在渲染時會出現什麼問題,以及如何在遊戲中提升渲染性能。利用這些知識和咱們的分析工具,咱們能夠修復與渲染相關的性能問題,並構建遊戲架構,使它們擁有一個流暢高效的渲染管線。
下面的連接提供了關於本文主題的進一步詳細信息: