Unity5內部渲染的最佳化1:介紹,unity5內部渲染最佳化
譯自aras的部落格,總共3篇文章,講述unity5最佳化自己渲染器的過程
吸取大神調試與最佳化經驗
在工作中我們形成一個小組“strike team”來最佳化unity渲染的cpu部分。
基本資料/父本的警告在很多情況下,我要嚴厲的說“這段代碼很爛!“。當要努力改善代碼的時候,你當然想提高不好的地方,這是重點。
一般來說並不意味著程式碼程式庫是壞的,或者它不能用於做出好東西。就在今年3月,我們有Pillars of Eternity, Ori and the Blind Forest and Cities: Skylines among top rated PC games; 這些遊戲都使用unity做的。“手機引擎只適合原型設計”這點還不太糟。
事實是,任何程式碼程式庫,在很久的時間開發使用,並只有很少的人使用,在某種意義上真是糟糕。他們是代碼奇怪的地方。沒有人記得它們是怎樣工作的,因為是在很多年前做的,沒有意義了,也沒有人來修複。在一個足夠大的程式碼程式庫,沒有一個人可以知道所有的關於它如何工作的詳細資料,所以在其他一些微妙的方式有一些決策衝突。借用某人的一句話“there are codebases that suck, and there are codebases that aren’t used” :)
努力改善程式碼程式庫是很重要的!我們一直在堅持在改善它。我們已經在所有的方面都做了大量的改進,但坦率地說,渲染代碼在近幾年改進的非常多,沒有人把單獨維護和提高帶代碼成一個全職的工作,但是我們做了!
好幾次我指著一些代碼,說“哈哈!那太愚蠢了!”這是我寫的代碼。或者是有各種因素的代碼(缺乏時間等)。也許我當時是愚蠢的,也許我在五年後會說一樣的話。
願望清單系統的高輸送量,沒有瓶頸的工作。
(Unity5.0)現在的渲染,,著色器運行&圖形API的CPU代碼並不是非常有效率。它有一些問題,需要我們儘可能多的解決:
1. 圖形加速器(Gfx)裝置(我們的抽象渲染API)
a.抽象主要是圍繞DX9 / 部分的DX10的概念設計。例如常數/統一(constant/uniform)緩衝現在並不適合。
b.過了幾年越來越混亂,有些地方需要清理
c. 允許並行命令緩衝區建立現代API(如consoles, DX12, Metal 或 Vulkan)。
2.渲染迴圈
a.許多無效的東西和多餘的決策需要最佳化
b.並行啟動並執行迴圈和/或它們的jobify部分 。如果可能的話,使用原生的命令緩衝建立API。
c.讓代碼更簡單更統一。分析出常用的功能。更多的可測試性。
3.著色器/材質的運行
a.資料在記憶體中的布局並不是特別好
b.想要清理複雜的代碼。增加可測試性。
c. “固定功能著色器(Fixed function shaders)”的概念在運行時不應該存在。見【Generating Fixed Function Shaders at Import Time】。
d.基於文本的著色器格式是愚蠢的.見【Binary Shader Serialization】
限制無論我們最佳化/清理代碼,我們都應該儘可能保持他們的功能可以工作。一些很少使用的功能或特殊的情況可能被改變或破壞,但這隻是作為最後的手段。
還有一點需要考慮,如果一些代碼看起來很複雜,它也許由於以下幾個原因產生的。其中之一就是“有人寫的代碼太複雜”(太棒了!我要簡化它)。另一個可能是“有一些代碼因為一些原因在過去是複雜的,但現在不是了”(太棒了! 我要簡化它)。
但是也有可能代碼做了複雜的事情,例如它需要處理一些棘手的情況。開始從頭“重寫”代碼很容易,但在某些情況下一旦你開始讓你的又新又好的代碼做舊代碼所做的一切時,它可能變得複雜。
計劃增加一段CPU的代碼,通過幾方面改善它的效能:1)“只是使它更快”和2)使它更並行。
我想首先關注“只是使它更快”部分。因為我也想簡化代碼,又想到要做很多棘手的事情。簡化資料,使資料流更加清晰,使代碼更簡單往往還是做第二步(“更並行”)更容易。
首先我會看著更高水平的渲染邏輯(“渲染迴圈(rendering loops)”)和材質/材料運行,團隊中的其他人將考慮簡化和處理抽象渲染API,並嘗試“更並行”的方法。
進行渲染效能測試,我們會需要一些實際的內容去測試。我看了現有的幾個遊戲和示範,讓他們的CPU被限制 (通過減少GPU負載-低解析度渲染;減少多邊形數,減少陰影貼圖;減少或消除後期處理;減少紋理解析度)。讓CPU有更高的負荷,我複製了部分情境,所以要比原來的渲染得更多。
渲染測試非常簡單,比如“嘿,我有100000個cubes!”但這並不是一個現實的例子。“只是大量使用相同材質的大量的對象”是一個非常不同的渲染情況,從用不同參數的成千上萬的材質,數以百計的不同的著色器,幾十個渲染目標的變化,陰影貼圖&定期渲染,對象的alpha混合,動態產生幾何體等。
另一方面,測試一個“完整的遊戲”也非常麻煩,特別是它有需要互動的地方,緩慢載入所有關卡,或不限制CPU。
當測試CPU效能時, 在多個裝置上測試很有協助。我通常在PC Windows 系統(i7 5820 k),在Mac筆記本(2013 rMBP), iOS裝置怎樣都好我現在在用 (iPhone 6)上進行測試。在控制台上測試將會很棒,我一直聽說到他們有很棒的分析工具,或多或少固定的時鐘和cpu——但我並沒有devkits。也許這意味著我應該弄一個。
注釋接下來,我運行了標準的項目看了分析的資料(包括unity分析器和第三方評測器不活躍的困/工具) ,也看了代碼看它做什麼。此時,每當我看到了一些奇怪的事情於是我把它寫下來供以後調查:
以下來自翻譯
setPassWithShader 在某些點來避免PPtr dererf的最佳化方式。現在他看起來總是做PPtr dererf,然後只要調用SetPass(又做了一次dererf)。
材質顯示列表不斷的重建>1的每像素光。不需要儲存多於一個列表的材質
GetTextureDecodeValues被調用了很多次(建立像素光cookie的一些東西),結束無用的線性gamma轉換
材質顯示列表不斷地重建由於未賦值的全域貼圖屬性而產生連鎖反應(_Cube ,沒有賦值),在我們的屬性丟失了的時候同時需要找出為什麼我們會失敗並做記錄
GpuProgramParameters::MakeReady是做什麼的?為什麼把它區分?
為什麼STL maps被屬性工作表使用?
1. 只使用健全的&簡單的資料布局
2. 相關的怪事:
1. 為什麼TexEnv 從屬性工作表中分離?
2. SetRectTextureID-為什麼,什麼
3. 貼圖 像素寬度/HDR 解碼效能的一部分在裝置狀態中執行
4. NotifyMipBiasChanged 有很多複雜的事情,原不明
IsPassSuitable在渲染迴圈中一次又一次的被調用。也許在渲染迴圈中要建立direct pass 指標表?
一次性提供所有貼圖取代在某一時間SetTexture
在記憶體中安排屬性工作表資料來匹配真實不變的緩衝布局。可能需要幾個不同關鍵的字布局
TextureID轉換為long(mac/linux中是64位)被ps4特殊提交3cbd28d4d6cd
1. 看起來像只在ps4上的最佳化,直接儲存一個指標到TextureID。如果可以的話無論任何地方都這麼做,或者只在ps4把它做成long(intptr_t更好)
為什麼ChannelAssigns 和VertexComponent在所有時間都能通過嗎?似乎沒有什麼用處
渲染迴圈分類是非常昂貴的。基於雜湊分配(渲染迴圈分配)開啟並完成它。
上面的一些缺陷可能由某些原因產生,在這種情況下,我添加註釋去解釋它們。可能當時有一些原因,但現在沒有了。在這兩種情況下源控制日誌/注釋功能是非常有協助的,並問寫代碼的人當初為什麼會這樣。上面的列表的一半也許因為我以這種方式寫很多年了,這意味著我必須記住這些原因,即使他們“在當時似乎是一個好主意”。
這就是介紹。下一次,會對上面的清單做點什麼!
下一篇待譯
------譯自 wolf96 http://blog.csdn.net/wolf96