1. 程式人生 > >渲染優化01

渲染優化01

ppi 不同 出了 head 系列 normal process man 組件

https://unity3d.com/cn/learn/tutorials/temas/performance-optimization/optimizing-graphics-rendering-unity-games (官方原文鏈接) 遊戲中的圖形渲染優化
  1. 介紹:
在這篇文章中我們將學習當Unity渲染一幀時,在場景背後到底發生了什麽,會出現什麽樣的問題以及怎麽去解決關於渲染的相關問題。 在讀這篇文章之前,應該要清楚的認識到:提高渲染性能的方案不是唯一的。因為影響渲染性能的因素有很多,包括遊戲自身,硬件和運行平臺。我們應該從調查、經驗以及嚴格的分析這些案列。 文章包含了很多普遍的渲染性能問題的解決方案建議以及拓展閱讀,很可能我們的遊戲就存在這些問題或者其他文章沒有提到的問題,但是這篇文章仍然可以幫助我們理解這些問題並且給出了有效的知識點和詞匯幫助我們快速找到解決方案。
  1. 渲染簡介:
開始之前,讓我們簡單快速的了解一下Unity渲染時發生的事情,了解這個渲染流程將會幫助我們理解、研究並且解決性能問題 NB:文章中將用“object”來表示在遊戲中被渲染的物體,任何帶有renderer組件的物體就會被叫做object 基本上,渲染可被描述為以下幾點: (1)CPU決定什麽會被繪制以及如何繪制 (2)CPU發送指令給GPU (3)GPU根據CPU發送的指令進行繪制 現在,讓我們更進一步的看看到底發生了什麽,在文章的後面將會詳細的介紹這些步驟,但現在先熟悉一下這些詞匯以及了解一下CPU和GPU在渲染中扮演的角色。 渲染管線是經常被用來描述渲染的,這是個非常有用的一張腦海圖,有效的渲染應該遵循以下幾點: (1)CPU會檢查場景中的每一個物體,決定他們是否會被渲染。只有符合條件(certain criteria)的物體才會被渲染;比如,攝像機的視椎體(view frustum https://docs.unity3d.com/Manual/UnderstandingFrustum.html?_ga=2.87818000.925366423.1538040429-1156207016.1532919959)可見範圍。物體不會被渲染的情況叫做裁剪(culled),想知道更多關於視椎體和視椎體裁剪知識請移步到該網頁( https://docs.unity3d.com/Manual/UnderstandingFrustum.html?_ga=2.103692168.925366423.1538040429-1156207016.1532919959) (2)CPU將會收集所有會被渲染的物體的信息並且對這些數據進行排序然後加進到命令集合中,也就是所謂的draw calls。一次drawcall包含的信息包括:單一網格數據和這個網格將會怎麽被渲染;比如,哪一張圖片會被使用。在某些情況下,那些共享設置的物體會被包含進相同的drawcall中,歸並不同物體的數據到相同的drawcall中的操作叫做合批(batching) (3)CPU為每一個drawcall創建一個被叫做batch的數據包,合批有時候包含的是數據而不是drawcalls,但這些情況不考慮。 對於每個包含drawcall的合批(batch),CPU必須做以下操作: (1)CPU會發送一個改變一系列變量(這些變量統稱為渲染狀態(render state))的指令給GPU。這個指令也叫做 SetPass call,一個SetPass call指令將會告訴GPU哪些設置將會被用來渲染下一個網格(mesh)。SetPass call只有在下一個要被渲染的網格跟上一個網格的渲染狀態不同時才會被發送。(A SetPass call is sent only if the next mesh to be rendered requires a change in render state from the previous mesh) (2)CPU發送drawcall指令給GPU,這個drawcall指引GPU去渲染用了 最近一次SetPass call設置的網格 (3)在某些情況下,合批需要多個pass,一個pass是shader中的一部分,一個新的pass需要一個渲染狀態的改變,對於合批的每一個pass,CPU都必須發送一個SetPass call並且重新發送一次drawcall指令。 與此同時,GPU需要做以下操作: (1)GPU根據CPU按順序發送的指令去處理相應的操作。 (2)如果當前的任務(task)是SetPass call的話,GPU就會更新渲染狀態 (3)如果當前的任務(task)是drawcall的話,GPU就會渲染網格。這個是分階段發生的,是定義在shader代碼中的(separate sections)。這部分比較復雜,暫且不詳細討論,但能讓我們了解到vertex shader(頂點著色器:它告訴GPU怎麽處理網格的頂點)代碼塊和fragment shader(片元著色器:它告訴GPU怎麽去繪制每一個像素)代碼塊。 (4)這個處理過程將會一直重復執行,直到GPU處理完所有從CPU發送的指令操作 到此,我們了解了當Unity渲染一幀時到底發送了什麽,現在讓我們考慮一下渲染中會發生的一些問題 渲染問題的種類: (1)要了解渲染就必須明白這一點:為了渲染一幀,CPU和GPU都必須完成各自的任務,如果任意一個任務的執行時間太長,當前幀的渲染都會被延遲。 (2)渲染問題有兩個基本成因,第一類是:低效的管線,這類問題的產生是因為在渲染管線中有至少一個任務的執行時間過長了,中斷了數據流。這類問題也被叫做:bottlenecks(瓶頸)。第二類問題是:塞入太多的數據給管線。即便是再有效的管線,在一陣幀內,也有處理數據量的限制。 (3)如果是因為CPU的任務的執行時間過長,這種問題叫做:CPU bound(限制),如果是因為GPU,那就叫做:GPU bound 理解渲染問題: (1)使用性能分析工具能讓我們在做出決策前了解到渲染性能問題的成因。不同的問題需要不同的解決方案。衡量方案的有效性也是非常重要的;處理渲染性能問題其實是一個權衡問題,也就是說,優化一個可能會給另外一個造成負面影響。 (2)Unity內置性能分析工具:Profiler window , Frame Debugger The Profiler Window:可以觀察到實時的渲染數據。包括:內存使用率,渲染管線和用戶腳本性能表現。關於The Profiler Window學習文章可以參考下面兩個網址: https://docs.unity3d.com/Manual/ProfilerWindow.html?_ga=2.129883652.925366423.1538040429-1156207016.1532919959 https://unity3d.com/cn/learn/tutorials/temas/performance-optimization/profiler-window?playlist=44069 The Frame Debugger:可以觀察到每幀的渲染情況,step by step。通過The Frame Debugger,我們可以查看很多詳細信息:比如在每一次drawcall中什麽被繪制了,涉及到的shader屬性和從CPU發送過來的指令順序。這些信息能夠幫助我們理解遊戲是怎麽被渲染的以及如何優化渲染性能。 關於The Frame Debugger的學習文章: https://docs.unity3d.com/Manual/FrameDebugger.html?_ga=2.131396487.925366423.1538040429-1156207016.1532919959 https://unity3d.com/cn/learn/tutorials/topics/graphics/frame-debugger 查找渲染性能問題的原因: (1)在我們嘗試為遊戲提高渲染性能前,我們必須確定由於渲染問題,遊戲會運行緩慢。如果是因為過於復雜的代碼造成的,很抱歉,在這裏沒有這個解決方案。如果你不確定當前的性能問題是渲染方面的,那麽可以查看這篇文章: https://unity3d.com/cn/learn/tutorials/temas/performance-optimization/diagnosing-performance-problems-using-profiler-window?playlist=44069 (2)一旦我們確定了性能問題是渲染方面的,我們應該弄清楚是不是CPU bound或者是GPU bound。不同的問題需要不同的解決方案,所以在修復問題之前應該先搞清楚問題的成因,如果你還不能確定是否是因為GPU bound或CPU bound,你可以查看這篇文章: https://unity3d.com/cn/learn/tutorials/temas/performance-optimization/diagnosing-performance-problems-using-profiler-window?playlist=44069 如果確定了性能問題是渲染方面的,並且也弄清楚了是否是GPU bound或CPU bound,那繼續下面的閱讀: 如果是CPU bound: 一般來說,CPU為渲染一幀所做的事情分為以下三種: (1)決定什麽會被繪制 (2)指令準備 (3)發送指令 這些廣義範疇包含了很多獨立的任務,並且這些任務會在多線程中完成。線程允許分離的任務同時執行,當一個線程在執行一個任務時,另外一個線程也能夠同時執行另外一個任務。這意味著可以更快的完成操作。當渲染任務被分到不同的線程中時,也叫做多線程渲染(multithreaded rendering) 在Unity的渲染流程中,有三種不同的線程類型:主線程,渲染線程,工作線程(或者叫輔助線程)。主線程是負責遊戲的主要任務的,也包括一些渲染任務。渲染線程是專門發送指令給GPU的。工作線程就是單獨負責各自任務的:比如,裁剪(culling)或網格蒙皮。線程會執行哪個任務取決於遊戲中的設置和遊戲運行的硬件設備。舉個例子:目標設備有更多核,那麽就會生成更多的工作線程。基於此,目標硬件設備的性能分析也是很重要的,不同的設備會有不同的表現。 因為多線程渲染是復雜的並且依賴於硬件設備,所以在進行性能優化之前必須要弄清楚哪些任務導致了CPU bound,如果遊戲是因為線程中的裁剪操作(culling operations)的執行時間過長導致運行緩慢的,那麽,在其他線程中發送指令給GPU也不會減少這個執行時間。 NB:並不是所有的平臺都支持多線程渲染,至少目前WebGL是不支持的。在那些不支持多線程渲染的平臺上,所有的CPU任務都會在相同的線程中完成。如果在這樣一個平臺上,優化CPU的操作就可以提高CPU的性能表現。如果遊戲中出現這樣的情況,那麽我們應該閱讀接下來的所有部分並且考慮哪一個優化方法是最適合我們的遊戲的。 Graphics Jobs(Experimental) 技術分享圖片技術分享圖片 在Unity中的Player Setting中的Graphics Jobs選項是決定Unity是否使用工作線程完成渲染任務的(不勾選的話就會在主線程完成,在某些情況下,是在渲染線程中完成)。當這個選項被勾選時,能夠極大地提高性能。如果要用到這個特征屬性,那麽我們就應該去分析勾選和不勾選狀態下的遊戲性能。 --找到導致渲染性能問題的元兇 --使用Profiler Window能夠幫助我們找到是哪個任務導致了CPU bound, 發送指令給GPU (1)發送指令給GPU所耗費的時間是造成CPU bound的最常見的原因。在大多數平臺上,這個任務的執行會在渲染線程中,在某些平臺上(ps4)會在工作線程中執行。 (2)最耗時的發送指令操作是SetPass call,如果我們遊戲是因為發送指令給GPU造成CPU bound的話,那麽減少SetPass call的數量是提高性能的一劑良方。 (3)我們可以通過Unity的Profiler Window性能分析工具觀察到有多少SetPass call和batches。有多少SetPass call會被發送很大程度上取決於目標硬件,相比移動設備,高端的PC機上會發送更多的SetPass call (4)SetPass call和與之相關的batches的數量取決於幾個因素,接下來的文章中也會深入的探討這個話題,然而,通常情況下是這樣的:
  1. 減少batches的數量並且/或者讓更多的物體使用相同的渲染狀態,在多數情況下,是能減少SetPass call數量的
  2. 減少SetPass call的數量,在多數情況下,會提高CPU性能
如果減少了batches卻沒有減少SetPass call,也會使性能提高的。這是因為相比多個batches,CPU能夠更加有效的處理單個batch,即使他們包含了相同大小的網格數據。 通常來講,有三種方法可以減少batches和SetPass call。我們將更加深入的探討這幾點: (1)減少需要被渲染的物體的數量可以同時減少batches和SetPass call (2)減少需要被渲染物體的渲染次數能夠減少SetPass call數量 (3)合並需要被渲染物體的的數據將減少batches 不同的技術會適應不同的遊戲,所以我們應該考慮所有的可能,以此決定使用哪一些技術。 減少需要被渲染的物體的數量: 減少需要被渲染的物體的數量是減少batches和SetPass call的最簡單的方式,下面給出幾個可以減少需要被渲染的物體的數量的技術
  1. 簡單地減少場景中可見物體的數量是一個非常有效的方法。比如,如果我們渲染大量的角色模型,那麽可以試驗一下少量角色模型的情況。如果場景表現有所提升,這將是更便捷的方案,而不是復雜的技術了。
  2. 通過攝像機組件的Far Clip Plane屬性降低攝像機的繪制距離。攝像機的可視距離範圍。如果希望遠處的物體看不見,可以嘗試使用這個:
fog to hide the lack of distant objects: https://docs.unity3d.com/Manual/GlobalIllumination.html?_ga=2.93584149.925366423.1538040429-1156207016.1532919959 技術分享圖片技術分享圖片
  1. 基於距離,為了更多細粒度的隱藏物體的方式,可以使用攝像機的Layer Cull Distances屬性,為不同層級的物體提供自定義的裁剪距離,如果有很多前景細節裝飾,那麽這個方式是很有效的;我們能夠在更加短的距離上隱藏這些細節。
https://docs.unity3d.com/ScriptReference/Camera-layerCullDistances.html?_ga=2.92118162.925366423.1538040429-1156207016.1532919959 。
  1. occlusion culling(遮擋裁剪,遮擋剔除)是使那些被其他物體遮擋的物體不被渲染出來的技術。比如場景中有一個比較大的物體,這個物體後面有其他的物體,那麽就可以使用occlusion culling(遮擋剔除)。Unity的遮擋剔除技術(occlusion culling)並不是適合所有的場景,它會造成一定的CPU開銷並且設置起來也比較復雜,但在某些場景中它是可以提高性能的。這有一篇學習occlusion culling的博客:
https://blogs.unity3d.com/cn/2013/12/26/occlusion-culling-in-unity-4-3-best-practices/?_ga=2.91616146.925366423.1538040429-1156207016.1532919959 除了使用Unity的occlusion culling,我們也可以使用自己的occlusion culling:手動地將一些玩家看不到的物體隱藏。就比如一些用來顯示劇情動畫的物體,在播放之前或之後是看不見的,就可以隱藏掉這些物體。這也告訴我們,有時候不一定要在Unity官方找答案,根據具體的遊戲找到適合的解決方式。 減少每個需要被渲染的物體的渲染次數: (1)實時光照技術、陰影和反射等技術提高了遊戲的真實性,但也是非常消耗性能的。使用這些技術將會使物體被渲染多次,這很影響性能。 (2)使用這些技術產生的具體影響取決於光照路徑(rendering path),渲染路徑是繪制場景時,計算的執行順序,渲染路徑之間的主要不同點它們怎麽去處理實時光照、陰影和反射。一般情況下,如果遊戲運行在高端的硬件設備上並且使用了大量的實時光照、陰影和反射,那麽Deferred Rendering是個絕佳的選擇。而Forward Rendering是在相反情況下的更合適的選擇。然而,這是非常復雜的任務並且如果我們希望使用實時光照、陰影和反射這些技術,那麽,研究相關課題和案例是非常有必要的。這個網址: https://docs.unity3d.com/Manual/RenderingPaths.html?_ga=2.159199410.925366423.1538040429-1156207016.1532919959 可以學習到這些技術知識,這裏提供了很多關於Unity可供選擇的渲染路徑之間的不同點的相關知識。而這篇文章: https://unity3d.com/cn/learn/tutorials/topics/graphics/introduction-lighting-and-rendering 提供了非常有用的光照技術知識 (3)不管選擇哪種渲染路徑,使用實時光照、陰影和反射這些技術都是會對遊戲造成一定影響的,所以,我們應該了解如何去優化它們
  • Unity中的動態光照是非常復雜的課題,所以深入探討這個話題已經超出了本篇文章的範疇。這裏提供兩篇學習文章(https://unity3d.com/cn/learn/tutorials/topics/graphics/introduction-lighting-and-rendering(動態光照介紹),https://docs.unity3d.com/Manual/LightPerformance.html?_ga=2.162255028.925366423.1538040429-1156207016.1532919959 (詳解普通光照優化))
  • 動態光照是非常消耗性能的,當遊戲場景存在一些不動的物體:比如,風景,我們可以使用一個叫做baking(烘焙)的技術去預先計算光照,這樣在實時光照時就不會再計算了,學習這個技術請移步:https://unity3d.com/cn/learn/tutorials/topics/graphics/lighting-overview?playlist=17102 而這篇文章:https://docs.unity3d.com/Manual/GIIntro.html?_ga=2.69613336.925366423.1538040429-1156207016.1532919959 詳細介紹了baked lighting (烘焙光源)
  • 如果想要使用實時陰影,這是能提高性能的好去處 。這篇文章 https://docs.unity3d.com/Manual/DirLightShadows.html?_ga=2.167572022.925366423.1538040429-1156207016.1532919959 指引我們在Quality Settings如何調整shadow屬性並且告訴我們這些是如何影響性能的。比如,使用Shadow Distance屬性來確定只有近處物體會計算陰影。
  • Reflection propes(反射探頭)能夠生成真實反射但這也是非常消耗性能的(增加batches)。應該盡量少的使用的反射,並且使用時盡可能的去優化。這篇文章https://docs.unity3d.com/Manual/RefProbePerformance.html?_ga=2.131305607.925366423.1538040429-1156207016.1532919959 (反射探頭優化)
合並物體數據,減少batches (1)在條件確定好時,一個batch會包含很多物體數據。為了能夠符合batching的條件,物體應該是:
  • 相同的材質的相同的實例共享
  • 材質設置相同(比如texture,shader,shader系數)
(2)批處理符合條件的物體可以提高性能, 雖然我們分析這些確定batching開銷的優化技術並不能很好的提高性能。(這句可能翻譯的不是很準確,原文是這樣的:Batching eligible objects can improve performance,although as with all optimization techniques we must profile carefully to ensure that the cost of batching does not exceed the performance gains.) 以下有幾點關於批處理符合條件的物體的不同技術
  • Static batching(靜態批處理技術)能使Unity批處理近處且不動的符合條件的物體。比如,一堆相同的物體(比如巖石)就可以使用靜態批處理技術進行優化。這篇文章:https://docs.unity3d.com/Manual/DrawCallBatching.html?_ga=2.102928520.925366423.1538040429-1156207016.1532919959 介紹了靜態批處理設置的用法說明。靜態批處理會造成較大的內存使用,所以在分析遊戲性能時不能忘了分析這個
  • 動態批處理是另一個能批處理那些符合條件的物體的技術,不論這些物體是否會動。使用這個技術會有一些限制條件。這些限制會被列舉出來,但與用法說明分開。這篇文章https://docs.unity3d.com/Manual/DrawCallBatching.html?_ga=2.94609172.925366423.1538040429-1156207016.1532919959。 動態批處理會影響到CPU使用量,並且它能節省的時間比開銷的時間更多。所以謹慎使用
  • 批處理Unity UI元素會有些小困難,因為會被UI布局所影響。文章
https://unity3d.com/cn/learn/tutorials/topics/best-practices/guide-optimizing-unity-ui?playlist=30089 () (3)GPU instancing()技術能夠很有效的批處理相同的物體,但有一些限制並且不是所有的硬件都支持。但如果遊戲中存在大量的相同的物體,使用這個技術會是個絕佳選擇。這篇文章:https://docs.unity3d.com/Manual/GPUInstancing.html?_ga=2.158048050.925366423.1538040429-1156207016.1532919959 (詳解GPU instancing) (4)Texture atlasing(圖集打包)技術可以合並大量紋理,在2D遊戲和UI系統比較常見,但也可以用在3D遊戲上。遊戲中制作美術資源時就可以用到這個技術,這樣我們就能確定物體共享紋理並且物體也是符合批處理條件的。Unity內置圖集打包工具(Sprite Packer) (5)手動合並共享相同材質和紋理的網格也是有可以的,要麽是在Unity Editor模式下要麽是在運行時通過代碼完成。當使用這種方式合並網格時,我們應該要知道陰影,光照和裁剪仍然會逐對象級運行;這意味著通過合並網格實現的性能提升會被那些當不被渲染時就不再裁剪的物體抵消掉(this means that a performance increase from combining meshes could be counteracted by no longer being able to cull those objects when they would otherwise not have been rendered ),如果我們想研究這個方法,那就去測試Mesh.CombineMeshes這個方法 (5)在代碼中使用Renderer.material需要註意。它會復制材質並且返回一個復制對象的引用。當這個renderer是合批中的一部分時,這樣做的話就會打破合批,因為這個renderer(渲染器)不再持有當前材質實例的引用了。如果想要在代碼中使用合批好的物體的材質,那就用Renderer.sharedMaterial. 裁剪,分類,批處理 裁剪,收集將會被繪制的物體的數據,整合這些數據到批處理中並且生成GPU指令都將會促成CPU bound。這些任務要麽在主線程要麽在工作線程中執行,取決於遊戲設置和目標硬件。
  • 就裁剪本身而言是不太消耗性能的,但是刪減掉不必要的裁剪也是會有助於性能的提升的。對於場景中所有激活的物體,包括那些不被渲染的層上的物體,都會有逐對象逐相機的日常開銷(per-object-per-camera overhead).我們可以通過disable相機並且deactivate或disable那些當前沒有使用的渲染器來減少開銷。
  • 批處理能夠很大程度上提升發送指令的速度(CPU發送給GPU的指令的速度),但有時候會在別的地方增加一些多余的開銷。如果批處理操作促成了遊戲的CPU bound,那麽我們就應該限制手動的或自動的批處理操作了。
蒙皮網格 (1)當我們通過網格變形生成骨骼動畫時就會使用到蒙皮網格渲染器,它通常會在動畫角色中使用。渲染蒙皮網格的任務通常是在主線程或獨立的工作線程中執行,取決於遊戲設置和目標硬件設備。 (2)渲染蒙皮網格是一個很消耗性能的操作。如果因為渲染蒙皮網格促成了遊戲的CPU bound,我們可以嘗試以下幾個方法提升性能:
  • 我們應該考慮是否每一個物體(當前已經用了SkinnedMeshRenderer組件的)都需要使用SkinnedMeshRenderer組件,有個情況就是,我們導入了一個使用了SkinnedMeshRenderer組件模型,但我們沒有給它動畫,像這樣的話,把SkinnedMeshRenderer組件替換成MeshRenderer組件是可以提升性能的。當我們導入一個模型時,如果在“the model‘s import Settings”中選擇了不導入動畫的話,這個模型就會使用MeshRenderer組件,而不是SkinnedMeshRenderer組件。
  • 如果只是在某些時候需要使用動畫(比如,在遊戲啟動時或僅僅在離攝像機一定距離時),我們可以換成網格細節更少的版本或者使用MeshRenderer。SkinnedMeshRenderer組件有一個BakeMesh功能,這個功能能生成匹配姿勢的網格, 能使不同網格或不同渲染器之間進行交換但卻不會造成任何視覺上的物體改變
  • https://docs.unity3d.com/Manual/ModelingOptimizedCharacters.html?_ga=2.87438096.925366423.1538040429-1156207016.1532919959 這篇文章給出了一些優化那些使用了蒙皮網格的動畫角色的建議。
  • https://docs.unity3d.com/Manual/class-SkinnedMeshRenderer.html?_ga=2.160772661.925366423.1538040429-1156207016.1532919959 這篇文章介紹SkinnedMeshRenderer,包括了怎麽去調整組件屬性等來提升性能。另外還有一些使用建議,需要記住,蒙皮網格的開銷是逐頂點增加的;減少模型頂點才能減少工作量。
  • 在某些平臺上,蒙皮會被GPU處理,而不是CPU,如果GPU有足夠的能力,這樣的選擇是具有實驗研究價值的。我們可以在Player Setting中為當前平臺和目標設備啟用GPU skinning。
主線程中跟渲染無關的一些操作: (1)許多跟渲染無關的操作會在主線程中執行,這意味著如果在主線程中遇到CPU bound了,我們是可以通過減少CPU花費在這些與渲染無關的任務上的時間來提升性能。 (2)舉個栗子,在遊戲中,某些時候主線程可能會執行一些很消耗性能的渲染操作和代碼操作,促成了CPU bound。如果在沒有失真的前提下我們能盡可能優化這些渲染操作,那麽就有可能在代碼側減少CPU的開銷以提升性能。 如果我們的遊戲是GPU bound的情況: 如果是GPU bound,那麽我們首先要做的就是找出是什麽造成了GPU的瓶頸,GPU性能通常會因為fill rate(填充率)而受限,尤其是在移動設備上,但跟內存帶寬和頂點處理也有關系。下面就測試一下這些問題,了解是什麽原因造成的,如何診斷以及如何修補。 Fill rate 所謂fill rate就是GPU每秒能夠渲染的像素數量。如果遊戲因為fill rate受限,這意味著遊戲每幀想嘗試繪制的像素超過了GPU能處理的上限。 檢查是否是fill rate導致GPU bound是很簡單的:
  • 進行性能分析並紀錄好GPU時間
  • 在Player Setting中降低顯示分辨率
  • 再次分析遊戲性能,如果性能有所提升,那麽就可以認為是fill rate造成GPU bound 的
技術分享圖片技術分享圖片 因為fill rate造成GPU bound的解決方法
  • 片元著色器是shader定義的一段代碼,它告訴GPU應該如何繪制每一個像素,GPU會為每一個需要被繪制的像素執行這部分代碼,所以如果這段代碼是低效的就會很容易增加性能開銷。復雜的片元著色器是造成fill rate問題的非常常見的原因
-- 如果遊戲中用了Unity內置的著色器,盡量去使用那些最簡單的最優化的著色器來實現我們想要的視覺效果。比如, https://docs.unity3d.com/Manual/shader-Performance.html?_ga=2.102053897.925366423.1538040429-1156207016.1532919959 the mobile shaders that ship with Unity 是高度優化的;嘗試去使用這個著色器看看是否不影響視覺效果並且提升了性能。這些著色器專門為移動平臺設計的,但其實也適用其他任何項目。在保證項目視覺效果的基礎山,在非移動平臺上使用“mobile”著色器是可以提升性能的。 --如果遊戲中的物體使用了Unity的標準著色器,應該要知道Unity是基於當前material設置去編譯當前著色器的。只有那些當前使用的特征才會被編譯。這就意味著,去掉那些,比如細節貼圖,能夠使得片元著色器代碼簡單些,進而提升性能。還是一樣,去調整設置看看能不能提升性能。 --如果項目中使用了定制的著色器,要盡量去優化。優化著色器是非常復雜的,這有兩篇優化shader代碼的文章: https://docs.unity3d.com/Manual/SL-ShaderPerformance.html?_ga=2.154442288.925366423.1538040429-1156207016.1532919959 https://docs.unity3d.com/Manual/MobileOptimisation.html?_ga=2.154442288.925366423.1538040429-1156207016.1532919959
  • 過渡繪制是指相同的像素被繪制了多次。這種情況是在一些物體在另一些物體上面繪制時發生的,並且這個很容易造成fill rate問題。為了搞明白過渡繪制,就要弄清楚Unity繪制場景物體的順序。物體的shader決定了它的繪制順序,通常是通過指定的渲染隊列。Unity利用這些信息嚴謹繪制物體,文章:https://docs.unity3d.com/Manual/SL-SubShaderTags.html?_ga=2.160756277.925366423.1538040429-1156207016.1532919959 另外,在繪制前,不同渲染隊的物體會使用不同的排序方式。比如,Geometry隊列下,Unity從前往後對物體進行排序,以減少過渡繪制。Transparent隊列下,是從後往前來實現想要的視覺效果的。Transparent隊列下的從後往前的排序是會增加過渡繪制的。過渡繪制是非常復雜的研究課題,沒有唯一的方法解決這個問題,但減少重疊物體的數量是關鍵點,因為Unity不能自動對這些物體排序。在Unity Scene窗口可以很好的研究這個問題,Draw Mode(繪制模式)能讓我們看到場景中的過渡繪制,進而確定怎麽去減少(過渡繪制),造成過渡繪制最常見的罪魁禍首是透明材質(transparent material) 、沒有優化過的粒子,重疊的UI元素,所以優化或減少這些是可行的。https://unity3d.com/cn/learn/tutorials/topics/best-practices/fill-rate-canvases-and-input?playlist=30089 (關於UI和overdraw指導)
  • 圖片效果也是很容易造成fill rate問題的,尤其是當我們使用多種圖片效果時。如果遊戲使用了圖片效果並且造成了fill rate問題的話,我們應該去調整設置或使用更加優化的圖片效果(比如高光(優化過的)),如果在相同的相機中使用多種圖片效果,這將造成多次shader pass。在這種情況下,合並這些圖片效果的shader代碼成一個獨立的pass塊是有益的。比如Unity的延遲處理堆棧:https://github.com/Unity-Technologies/PostProcessing/wiki 如果經過優化後的圖片效果還是會有fill rate問題,那麽就要考慮不用圖片效果了,尤其是在低端的設備上。
內存帶寬 (1)內存帶寬是指GPU從顯存中存取的速率。如果我們遊戲受到內存帶寬的限制,這意味著我們使用了太大的圖片,超過了GPU的快速處理能力 (2)檢查一下是否是內存帶寬的問題:
  • 對遊戲進行性能分析並且紀錄GPU消耗時間
  • 在Quality Setting中設置好目標平臺的目標質量,降低紋理質量
  • 重復第一個步驟,如果性能提升,那麽就能確定是內存帶寬的問題
如果內存帶寬造成性能問題,那麽就要降低紋理的內存使用率了,下面有幾個方法能夠優化紋理
  • 紋理壓縮技術:能夠紋理的磁盤存儲大小和內存大小。如果內存帶寬在我們遊戲中會造成性能問題,使用紋理壓縮技術是可以提升性能的。在Unity中有多種紋理壓縮技術,每一種都有單獨的設置界面。通常來說,某些紋理壓縮是無論何時都是會被用到的,然而,一個試驗和錯誤能夠讓我們找到每一個紋理性能最好的最佳設置。請看這篇文章:https://docs.unity3d.com/Manual/class-TextureImporter.html?_ga=2.191165986.925366423.1538040429-1156207016.1532919959
  • 紋理映射(MipMaps)是優化方案中效果沒那麽好的技術,在Unity中可以用在遠處物體上。如果場景中有跟攝像機距離比較遠的物體,使用mipmaps能夠緩解內存帶寬帶來的問題。The MipMaps Draw Mode(https://docs.unity3d.com/Manual/ViewModes.html?_ga=2.103665800.925366423.1538040429-1156207016.1532919959)(Scene Mode下),能夠看到使用MipMaps後的好處,還有這篇文章:https://docs.unity3d.com/Manual/class-TextureImporter.html?_ga=2.199507494.925366423.1538040429-1156207016.1532919959
頂點處理 (1)頂點處理是指GPU必須渲染網格頂點的過程。頂點處理的性能開銷受到以下兩個方面的影響:會被渲染的頂點的數量,在每一個頂點上執行的操作的次數 (2)如果遊戲是GPU bound的話並且我們確定不是fill rate或內存帶寬的問題,那麽很可能就是頂點處理的問題。在這種情況下,嘗試減少GPU要執行的頂點處理的次數是可以提升性能的。 (3)下面介紹幾個可以減少渲染的頂點的數量或在每一個頂點上執行的操作的次數的方法
  • 首先,要減少那些不必要的復雜網格。如果使用了那些在遊戲中看不見的網格或因為生成時錯誤的有大量頂點的無效網格,這會讓GPU做很多無用功。降低頂點處理的開銷的最簡單的方法就是在我們的3D項目中生成更少頂點的網格
  • normal mapping(法線映射)技術是指紋理用在了可以生成龐大的復雜幾何結構幻覺的網格上的技術。一些GPU的性能開銷使用這個技術是可以提升性能的。https://docs.unity3d.com/Manual/StandardShaderMaterialParameterNormalMap.html?_ga=2.123024131.925366423.1538040429-1156207016.1532919959, 這篇文章指導我們使用normal mapping(紋理映射)來在網格上模仿復雜的幾何結構。
  • 如果網格中沒有使用紋理映射技術,一般會在網格的import settings中將vertex tangents(頂點切線)禁用掉。這種數據上的削減會設置到每一個頂點上
  • LOD(Level Of Detail 細節層次渲染)是一種可以降低遠離攝像機的網格的復雜度的優化技術。降低需要渲染的頂點數量且保證不失真。https://docs.unity3d.com/Manual/class-LODGroup.html?_ga=2.200163238.925366423.1538040429-1156207016.1532919959
  • 頂點著色器(shader代碼塊,告訴GPU如何繪制每一個頂點),如果遊戲因為頂點處理受限,那麽降低頂點著色器的復雜度是可以提升性能的
結論: 我們已經學習了在Unity中是如何渲染的,產生的問題的分類還有如何去提升渲染性能。利用這些知識加上性能分析工具,我們可以解決渲染性能問題並且使遊戲在一個高效流暢的渲染管線中構建。 擴展閱讀鏈接 https://unity3d.com/cn/learn/tutorials/topics/best-practices/guide-optimizing-unity-ui?playlist=30089

渲染優化01