您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何使用Unity 2018深度優化渲染管線”,在日常操作中,相信很多人在如何使用Unity 2018深度優化渲染管線問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何使用Unity 2018深度優化渲染管線”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
這段時間一直沒有出文章,其實除了學校課程繁忙這些因素,更是把許多時間都放在了整理和優化舊代碼的工作上,其中很大一部分包括對內存管理的“苛求”,乃至使用一些新特性對渲染管線的在CPU執行的代碼進行深度優化。
首先來介紹一下Unity現有的3種編譯方法,Mono VM, IL2CPP, Burst。
Mono虛擬機是傳統而古老的實現方案,其跨平臺的優勢使得早年的Unity一直依賴于它,然而Mono本身性能極差,代碼執行效率只有Native C++的一半左右,這也使得早年的Unity扣上了“做PC不行”“做不了大作”“引擎性能差”的帽子,而現在除了Editor中為了開發效率仍在使用Mono VM,PC,主機,手機等平臺對其的需求越來越低。
IL2CPP是通過IL作為中間層,將代碼編譯成Native CPP的辦法,在2018.3正式版發布以后,IL2CPP的編譯優化有了長足進展,一些古怪的小問題也逐漸減少,官方也大力推薦在高端平臺代替Mono使用,這將會是我們目前使用的主要的編譯方法。但是這種編譯方法也并非沒有坑, 相反它的坑要比Mono VM還要多。因為經過IL層的翻譯,一些C#高級用法在翻譯的過程中極有可能產生額外的性能消耗,所以在開發時我們應該用CPP的思維去考慮開發,畢竟游戲開發與傳統.Net開發還是有巨大差距的,當然也不是要求所有的開發者都像我一樣代碼里指針亂飛,把C#當C++寫,但是最起碼編程習慣方面的問題還是要留意一下的,該犧牲開發舒適度就要犧牲一下下(笑)。
Burst Compiler是一個專用于科學運算的編譯器,之所以說是“專用于科學運算”,是因為除此之外它基本沒有其他功能,本身不支持托管類型,所以代碼中不能有任何訪問托管類型的代碼,既然托管類型不支持,那么抽象,多態等面向對象就更別想了,可以說就是最原始的C語言編程,Burst的設計是專門用來處理科學運算的,比如處理矩陣,向量等提供了諸如SIMD之類的優化黑科技。官方一直在神化Burst,稱其運行性能能遠超C++,但是其實在我看來,Burst只在處理個別數據處理類方法時可以派上用場,在處理其他普通代碼時并不會比C++快多少,同時只允許使用非托管類型,禁止一切OOP對于項目開發而言許多時候也不切實際(至少在ECS全面普及開來前是這樣),所以Burst在目前只會被我們當成開發時佐料。
而我們主要的優化思路基本分為以下幾種:
盡量使用非托管的數據類型,并手動管理內存,做到真正的Runtime 0 GC。
盡量把C#當成C++來開發,原因上方已經介紹過。
盡可能多的使用Job System計算邏輯,充分發揮多線程優勢。
嘗試使用Burst Compiler編譯純數學運算的代碼。
在之前文章中介紹的GPU Driven Pipeline的代碼其實有很大的優化空間,因此我們首先處理的就是光照部分,因為我們的管線使用了Tile/Cluster Based Deferred Rendering,所以需要在CPU中準備傳入Compute Buffer的數據,同時燈光陰影的運算需要生成視錐體的矩陣,那么這一部分的運算就可以扔到Job System中進行運算,當然這一部分中有不少訪問托管類型的部分,而且運算也大多數是邏輯而非運算,所以這里使用傳統的IL2CPP編譯而非Burst,代碼結構大概是這樣:
因為整個渲染管線都是單例的存在,因此直接使用static非常安全,另外Job System中是不允許出現實例化的變量,所以這里也只能使用靜態,如果不想使用靜態,但又一定要使用托管類型,也并非沒有辦法,我的解決方案如下:
這樣的方法可以將托管類型的地址強行賦值到指針中,但是指針并不在GC的管理范圍,所以在這樣處理時一定要注意,是否應該開啟GCHandle保證非托管代碼執行的途中該托管類型不會被GC干掉。當然,Unity的Component是受引擎管理的,所以不需要考慮C# GC會作妖。
可以看到,在上方的Job中,處理了包括點光源,聚光源的信息和矩陣,并將運算結果轉存到上方的NativeArray和NativeList中,最后再將值直接傳入到Compute Buffer中,這個過程的計算一直是一個邏輯重點,比如Cubemap需要計算6個面的View projection Matrix。在將這一部分代碼放入到Job中后,在本人的高端PC測試機上,在燈光數量較多的時候,代碼執行時間直接減少了0.1ms-0.2ms,這是一個可喜的進步,尤其是項目開發后期主線程的壓力可能會成為性能短板,這樣的優化是非常有意義的。
除此之外,還有一個運算的大頭,就是Cascade Shadowmap的矩陣布置,Cascade Shadowmap的計算方法,這里已經不需多做解釋了,其中耗費較高的就是一個通過Frustum Corner獲取正方體位置,以及通過ViewProjection的逆矩陣反推世界坐標,進而計算像素的棋盤格,防止因為攝像頭移動導致的shadowmap鋸齒抖動,而逆矩陣的推導看似其貌不揚,實則運算量頗高,在CPU單核運算能力較弱的平攤上消耗絕對不容小覷,因此我們將其放到Job System中是一個很不錯的選擇,值得一提的是,這個過程屬于純數值類運算,因此可以使用Burst Compiler:
傳入一大堆需要用到的或者返回的屬性值,最后在Execute中計算,值得一提的是,Unity現有的GL.GetGPUProjectionMatrix是不支持Burst也不支持在分線程中運算的,這個真的很坑,所以我們只能自己寫一個D3D和OpenGL投影矩陣標準轉換的函數,同時因為Burst不支持靜態變量,所以連isD3D這種flag也需要手動傳遞(一種隱隱的蛋疼感傳來)。
雖然因為Burst的鬼畜限制,代碼變得奇丑無比,But it just works well! 計算部分的代碼大概如下:
總共4個cascade,因此這段代碼將會在4個線程中分別執行,這段代碼又為我們省下了主線程的0.1ms。
燈光數據的運算基本是整個渲染管線中消耗最大的一部分了,畢竟SRP已經將更復雜的場景剔除之類邏輯封裝好了,所以實際需要我們做的大概就是這些自定義的數據類型,一些自定義的Volume組件的視錐體也是需要我們手動計算的,比如之前Froxel volumetric Rendering中曾經提到的Fog Volume,也就是管理局部霧效的組件,我們當仁不讓的也使用了Burst Compiler加速:
其他大大小小的Job還是有不少的,Unity的Job System的原理是將任務累計到一起,通過調用JobHandle.ScheduleBatchedJobs()的方法同時開始計算所有任務,這是因為開啟線程本身是一個相對昂貴的步驟,所以我們應該盡量把更多的任務統一堆到Job Queue中,再讓執行流程負責啟動任務,最后通過Complete保證當前任務執行完畢,保證線程安全。綜上所述,Job System的使用本著:“益多,益雜,不益散”的原則,多,是因為每個struct在加入隊列的時候需要memcpy,因此體積不應該太大,同時較多的任務也更容易讓Unity的Job Threads自行挑選,平衡核心負載,益雜的意思是,許多大大小小的任務,積少成多,應該盡可能的寫成Job而不是堆砌在主線程,不益散則映射了剛才說過的ScheduleBatchedJobs的原理,應該盡可能同時啟動所有的任務,保證執行的統一性。
除了多線程優化,在內存優化方面我們也是完成了許多的工作,首先值得說的一點就是Unity的Memory Allocator的使用方法,學過C語言的朋友自然知道Malloc和Free的用法,這里就不用多說了,Unity提供了3種Allocator,分別是Temp, TempJob和Persistant。
Temp: 開辟的內存會在幀結束后被釋放,適合只在當前幀使用的,同時也是開辟速度最快的,可以說其開辟速度僅次于棧內存stackalloc,如果為了處理幀內傳遞的數據,應該盡可能使用這種,而且不需要Free,在幀結束后會被Allocator自己回收,沒有泄露危險。
TempJob:開辟速度僅次于Temp,可以維持4幀,在4幀后會被自動回收,坦率的講我個人不是很喜歡這個設定,因為“4幀”這個限制顯得有點不倫不類,但是存在即是正義,想必也有其用途所在。
Persistant:永久的開辟內存,開辟速度最慢,必須手動調用Free,否則會泄露,這也是最傳統的malloc方法,適合常駐的數據結構使用。
配合C# 7.3給出的unmanaged的泛型限制,可以使用這套內存管理系統寫出純手動管理內存的泛型,真正做到0 GC。然而目前引擎本身還是有一些限制,譬如SRP還不支持獲取ECS的Component類型,因此代碼還很難做到純粹的“面向性能編程”,總是顯得有些不倫不類,還帶著許多面向對象的包袱,希望在接下里幾年里,Unity能夠逐漸完善ECS和SRP,并逐漸徹底拋棄傳統面向對象的開發方式。
到此,關于“如何使用Unity 2018深度優化渲染管線”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。