程序丨Unity大牛姚霄凌:Unity 5.6+版本中GPU Instance研發實操

2021-03-02 騰訊GWB遊戲無界

關於Unite
Unite大會是由Unity舉辦的全球開發者大會,至今已有10年的歷史。Unite現已成為遊戲行業,VR/AR行業中最具有權威性和影響力的活動。

本文是Unity研發大牛姚霄凌在Unite大會的演講,以下是正文:

來的人比我想像中多一點,早上很早,自己也很早起來。我看大家今天有一整天都在這裡。我就講一下發布一年多來,我們做了什麼。將來還準備做哪些。

先介紹一下我們自己,Terrain Team有兩個人,都是在上海,我還有蔡原新,今年我還是要自己講一下。我們Terrain Team最近就集中在這個上面,新的功能暫時就不做了,就只是修一下Bug。平時的話做一些引擎底層的優化。

一、GPU Instancing:The past year…

過去一年Unity 5.4剛發布,5.5快要發布的時候,那個時候是我們GPU Instancing第一次在5.4裡面發布,那個時候大家用得比較少。這些新的功能為什麼要去做?也是因為在Unity官方論壇上收集到了很多,主要是國外開發者他們用了Instancing之後,他們的一些問題。然後其實開發者那邊比較難解決,還是需要我們自己引擎這邊需要為大家做一些事情。其實就相當於大家提問題,我們幫著做。

1、新API:DrawMeshInstanced

最重要的就是5.5添加了新的API(DrawMeshInstanced),是大家呼聲最高的一個,它本身很簡單,就是從函數的籤名可以看到,這種材料是shader,關鍵是你提供了一個數組,這個數組就是每個Instance的變換矩陣的取證。其實很簡單。變換矩陣大家應該很熟悉,可以用TRS這個函數來生成出來,我們為什麼要去做它?好處有很多,原來5.4剛剛發布Instancing的時候,純粹是靠renderloop裡面,我們添加了一個Instancing。意思就是說你要畫五百個Instancing,你就必須要畫五百個GameObject,這個開銷是非常非常大的。很多情況下你放了那麼多GomeObject,你的CPU開銷就上去了。這樣整體來說CPU這邊會有一點點提升,但是會很小。有了這個GPU  Instancing,首先renderloop不是很穩定,經常哪一個點就break了,也不知道為什麼。但是新的版本裡面加了一個功能,可以知道上一個和下一個沒有連在一起的原因。

那麼多的Instancing如果放在renderloop裡面,每一幀會去跟,這個開銷也全都省了,用一個API就好了。我們肯定是保證它的GPU性能是一樣的。有了這個之後,至少看到論壇上大家真正想用Instancing去做一些系統的,比如說我看到一個遊戲是太空中很多飛船戰鬥的一個場面,有很多雷射biu  biu  biu的那種,然後很多飛船都是用Instancing來做。他們反饋下來是CPU可以節省非常多的時間。壞處就是這個API不是那麼好用,你需要自己手工來寫代碼。因為就只是其中Unity一個數組,這個裡面Unity引擎是不知道的,所以它視錐剃除不會去做。比如說照相機轉開了,看到五百個Instancing裡面只有五十個。如果你需要消除這方面的影響,你需要自己做完剔除再提交,就比較麻煩一點。然後z-efficiency就是排序的問題,一般的話它可以遮擋住後面的物體。如果你想要獲得比較好的z-efficiency,你需要自己去做排序。還有就是每一幀要去調開,之前的邏輯很長,開銷也是蠻大的。

2、新API:DrawMeshInstancedIndirect

這些是問題,然後以後會解決,大家可以看到我們最後我們會有一個新的系統嘗試去解決一些新問題。現在暫時網上我們asset store裡面各種示例可以參考一下,它們做得很好。剛剛那個是5.5的,5.5應該有蠻多的用戶。5.6裡面新加了這個,就是Indirect。Indirect在於兩點:一個是不直接從GPU去生成Instancing的數據,不再是由CPU去告訴GPU數據是怎麼樣;另外一個是也不由CPU去告訴GPU要畫多少個Instancing。這些CPU全都不知道,CPU只是告訴GPU這裡可以看到ComputeBuffer對象。你從這個裡面去拿參數。五個參數,所以這塊ComputeBuffer很小,CPU只是告訴GPU你去從這一塊buffer裡面生成一個drawcall出來,其他的Instance數據從哪裡來都是shader會自己算出來。所以這個函數的籤名就是這樣子,可以看到,這個aabb其實對於API本身來說不是很需要,現在只能暫時放在裡面,以後renderloop重構之後就可以去除了。aabb你們開發者可以知道,你們可以把這個包圍盒告訴unity,然後可以做一個很簡單的camera的視錐剔除。它是需要API去支持的,所以需要你跑在D3D11以上。包括openGL4.3是不支持的,然後安卓的話需要GLES3.1。我相信一年半年以後就可以了,然後IOS Metal應該是支持的。

這個函數跟剛才那個相比,它主要的每個Instancing數據不是CPU告訴它的,而是通過shader裡面過程化地去生成。裡面包含兩個點,一個是用Instancing 0、1、2、3、4你需要過程化地去算出來。你一排的cube,地理坐標一個一個排序,用Instance Id可以去乘20。或者是你可能會有另外一塊buffer,這一塊是裡面放了Instancing的數據,你的shader只是去讀這個數據,然後把它附給每一個Instance。所以這個整合GPU Compute是最合適的,不需要CPU和GPU之間互相讀來讀去。原來那個就很沒有效率,如果你真的能去掉這一塊,相當於你去CPU、GPU做同步,就浪費了一幀兩幀的時間。另外移動平臺芬蘭那邊同事測出來,用Compute還太早。Compute本身可以,但是切換時間還比較久。PC上可以,安卓大多數不支持。

3、新的工作流程:5.6

5.6裡面還有一個新的變化,就是新的work flow,原來你要用的話就比較要建一個shader,5.6裡面一般來說不需要。你點上去,最底下會有選項,你勾上就有Instancing。你如果使用standard  shader或surface shader就可以。

相關的還有shader variant stripping。你比如說player的時候,本身shader保存在磁碟上還是蠻費空間的。現在5.6裡面相當於你不用的話都可以被strip掉。策略很簡單,build player的時候會去分析你build的場景。用了這個shader的material,只要用了任意一個,Instancing的variant就會被包含在最後的裡面。在Graphics settings裡面,這些都放在一起會有一個全局的控制。你可以把它強制給strip掉,這樣最終出來的就不支持Instancing。

剛才說那些都是在在線的文檔裡面。第一個草稿其實就是我們原來的5.4的文檔。現在是一個草稿,就算是草稿,你也可以去這個網址上去找,專門有一個草稿欄。關鍵是這個新的API的這個文檔我丹麥同事幫著寫了,裡面有很多的例子,大家可以看一下。

二、GPU Instancing:2017 and forward

1、「GPU Instacing 2.0」

接下來就是講除了這些之外,那些是已經發布的功能。接下來是我現在正在做的,可能在下一個版本,下一個版本2017.1可能delay。2017.1之後的版本,我講的這個應該會上去。GPU Instancing 2.0就是我們在論壇上說到更多別的反饋,或者是我們內部demoteam。我們自己在做地形和數相關的demo,他們也碰到一些問題,然後會要嘗試去解決它。第一個其實也很傻,大家知道Instancing的編譯速度很慢,原來是我們用了很長的數組,我自己測試下來,只是單純用微軟FXC,去編譯一個,一秒和十三毫秒的差別。應該是微軟shader的問題,我們去聯繫過他們,他們這個問題不準備修了,需要我們自己來解決這個問題。

因為有這個,所以你們添加新的Instancing的時候,你們需要考慮,你們每添加一個就相當於多了一個數組,編譯速度就越來越慢。我之前試過,我加了七個然後去編,就很慢。除了編譯速度之外還要考慮constant buffer的大小限制,它不是任意長度。規定是64kb,所以你的數組一多,或者你的數據一多,你需要把它考慮分在其他的constant buffer裡面,比較麻煩,要自己去算。然後maxcount決定了你有多大,比如說你的size是五百,你就算畫十個Instancing,你也是上傳一個五百的buffer,總線帶寬是很浪費的。所以之前你可以在shader裡面制定maxcount有多大,就評估你大致有多大,可以設一個值,這樣的話可以減少一點浪費。但是很難保證在所有情況下都能最大優化。maxcount在OpenGL下,它只有其他平臺的四分之一。為什麼呢?因為openGL下面,它的標準只保證有16KB的標準。一塊顯卡可能只有64KB,但是我們不知道。它之前是寫死在shader裡面,我們只能用一個最小的值。剛才說了,Instancing物體不支持全局光照,因為它需要7個數組,現在不是很現實。所以現在的Instancing都不支持lightmap。我們就會去嘗試去解決剛才所有問題。

目標首先不應該需要為shader的編譯速度擔心。可能之前根本就沒有擔心過,一般來說都非常快。我們希望你們不用再去考慮什麼是maxcount,就是那個數組大小,你們不用去想你這個項目多數情況下能有多大,根據這個再反過來修改shader。這個工作流上就會很慢。我們也希望不要佔太多的總限帶寬。理想情況下你畫五百個Instancing,你就傳五百個,你畫五十就傳五十,我們希望能支持到GI。

方案首先第一步要解決Instancing buffer的布局,有了布局之後,我們相當於把多個array合併成一個array,這樣子才可以動態改變它的長度。不然一個array本身就是一個一個單獨的array,要改變長度的話中間會空出來很多。沒有辦法縮小Constant Buffer它本身的size。另外一個就是用Out-of-Bounds Reads,聽上去蠻嚇人,其實它不會crash,數組長度不用等同於真正綁定的CB(Constant Buffer)大小。

2、Array-of-Struct

先講Array-of-Struct,上面一張是我們現在的布局。可以看到兩個Instancing的數據,一個是藍色world,另外一個橘黃色的是colour。先是五百個world,然後再放第二個Instance數組。代碼上來解釋就更加簡單一點。

知道Unity的人可能會知道,Unity現在shader也不支持寫struct。我們底層代碼要改得很多,要改到能夠正確讀出來每一個struct member的offset。改成這樣以後,我們需要新的宏來聲明。前兩天才剛做完GPU的開銷的測試,我本來期待它要比SOA要好一點,但是事實上完全一模一樣。我猜應該是跟它本身上面的實現方式,硬體這一塊我也不是很懂。如果在座的有知道這一塊的話可以教一下我。它的開銷我要介紹一下,所謂的開銷我們不是去測量用不用Instancing,我們測量的是說,比如說你同樣是訪問一個東西,你從array裡讀出來的性能和你直接去用單個的uniform去讀,相差多少。原來的和SOA是一樣的。CPU端性能更好。CPU端本身Instancing性能就已經蠻好的,但是更好的話相當於大家可以塞更多的遊戲相關的代碼到遊戲裡面。所以還是有一點用處的。

3、Out-of-Bounds Read

另外一個就是Out-of-Bounds Read,顧名思義就是越界讀數組。shader裡面就是數組聲明地越短越好,我們現在用的是2,為什麼不用1。1的話就是只有一個,所以它每次你都只讀到第一個元素,就算你用Instancing ID去讀,每次都是去拿之前的數據,就一定要用2。它會生成出來,我們綁定InstanceBuffer的時候,去讀沒有問題。D3D和Opengl都是支持的,但是Vulkan不支持。其他平臺有可能有些平臺會支持,我不確定Metal支不支持,但是我相當確信支持,但是還沒有來得及做。如果不支持那怎麼辦?我們想你shader在編的時候還是用長度是2,但是編完之後,因為用的是D3D11的compier,其他平臺的都是翻譯過去。所以可以修改數組長度。

性能上會比沒有它要慢一點,但是會和原來一樣,所以也不算太慢。最主要因為是長度只有2,所以大大縮小了shader的編譯時間,大約100×左右的倍率。然後可以在運行時根據批次大小動態調整綁定的constant buffer長度,節省帶寬。我們去調整一個size,因為新的buffer還是要create出來,所以不能每個不同的batch size都創建新的buffer。我們會去取,比如說50個、150個、200個,這個暫時還沒有定。用戶就不需要再去算你的CB Max Size有多大。Open GL的問題就解決了,可以拿到它支持的uniform block的大小,可以獲得跟其他平臺一樣的大小。

4、全局光照支持

有了這個以後,可以做全局光照。我們可以會去做一下壓縮,把float壓縮一下。然後lightmap  ST的4個float還是很小的,貼圖還是要一樣的。但是在lightmap的位置就可以不一樣。

5、其他改進

其他的話CPU端的代碼要更優化,並且支持多線程。在CPU端的開銷更加小。相當於來說我會去做半精度浮點類型的優化,這個的話API D3D11都是不支持的。我們現在自己去填buffer,所以可以這樣優化。還有就是寫數據在constant buffer會更緊湊一點。

6、結論

結論就是CPU端的開銷更加小,GPU端的開銷在某些情況下更小,這個某些情況就是指你整體瓶頸在於總線帶寬的時候,GPU端開銷會更加小。然後修改shader更快,支持全局光照。大部分功能正在測試,例如2017.2。這些做完,其實差不多就做完了。

7、BatchRenderAsset

我們為什麼要去做Instancing?做這個就是為了做BatchRenderAsset,用於大規Instancing渲染,比如森林、草地、城市建築等,用來取代地形的植被系統。目標就是最小化的rendreloop開銷,支持culling、LOD和streaming。所有的這些跟渲染相關的行為都是會由用戶提供的material來決定,你可以使用筆刷工具在場景的任意表面上刷Instance。然後最快今年下半年確定roadmap。要先確定roadmap,有了它以後,會第一時間跟大家分享。之後才會做,這是一點。

8、SpeedTree

另外一個SpeedTree,不會做新的feature。我自己不會做,但是其他的比如GI team會幫著做。所以在2017.1的下一個版本會做,暫時只有靜態的烘焙。還有地形上靜態陰影的一個烘焙。

9、Terrain

terrain本身不好的就是還沒有關於地形系統的計劃,中間變了很多,但現在暫時沒有計劃。好的一點是我們新招了一個很厲害的娜姐,她原來是在Bungie裡面做引擎架構,她沒有加入前就已經影響我們很多。她可能會建一個新的團隊,會重新安排一下地形系統,她也會看一下SpeedTree。

今天就是這樣,謝謝大家!

相關焦點

  • 【程序新手】Unity動畫系統之Animator學習篇
    新建unity工程,將模型文件導到Assets目錄。選中模型,在Inspector窗體可見Rig裡我們可以設置動畫類型,默認設為Generic。如果是要使用Mecanim提供的動畫retargeting等功能,那就需要將動畫類型設為Humanoid。要設置循環播放該動畫,勾選Animations裡Loop Time即可。
  • Unity免費的優質場景資源
    該資源包2.4G,項目需要使用Unity 2018.2b9或更高版本。請將其導入空白項目中使用,以避免與內置軟體起衝突。Hand Painted Nature Kit 完整版本包含了森林,果園,沼澤,草原等場景,但免費包中僅包含森林場景的一小部分,但是使用該資源包,你可以足夠在遊戲中創建一個漂亮的森林場景。
  • 《天涯明月刀》用Unity實現GPUDriven地形!
    unity本身的地形也是支持drawInstance的,他是用32x32的模型去draw的,並且有一點極其傻逼的地方是他處理不了不同lod之間的接縫問題,居然想到用六種模型去模擬所有的接縫情況,也就是說即使勾上了DrawInstance,Unity渲染一塊地形也得6個drawcall
  • Unity3D 2017
    3、點擊Next。5、點擊Next。6、點擊Browse更改安裝路徑,建議安裝在除C盤以外的其它磁碟,可以在D盤或者其它磁碟創建一個unity2017文件夾,然後點擊Next。11、雙擊打開D盤中unity2017文件夾。
  • UNITY-Fungus學習(上)
    [2]2019年使用Unity製作的遊戲和體驗已在全球範圍內覆蓋將近30億臺設備 [4] ,月均下載量超過30億次 [5] 。並且其在2019年的安裝量已超過370億次。[4] 全平臺(包括PC/主機/行動裝置)遊戲中有一半都是基於Unity創作的。
  • 如何運用Unity製作VR全景漫遊?
    看文百篇,不如實操一遍。今天給大家分享的是通過Unity製作VR全景漫遊的過程與方法,大家感興趣可以動手試試。
  • 如何在 Ubuntu 16.10 的 Unity 8 上運行老式 Xorg 程序
    這是因為在這個大家最愛的 Linux 發行版的最新版本中可以體驗其帶有的試驗性桌面。桌面發行版是人們最熟悉的 Unity 環境,但有一點點不同。它不再使用 X11 圖形技術,Ubuntu 的開發者選擇了另一種截然不同的方式。原來,Unity 8 用的是 Mir,這是 Ubuntu 為了在 Linux 上提供顯示服務而做出的努力。
  • Unity VR全景漫遊
    Unity5.3.1 X64 http://unity3d.com/cn/get-unity/download/archive
  • 遊戲動作師使用Unity3D遇到過的所有問題及解決方法
    導出相關圖為max導出fbx界面,各版本界面略有不同功能相近,版面所限請放大查看在遊戲美術工作流中,模型和動畫的導出都是由動畫師負責,一份可用的動畫文件,必須同時包含模型和動畫文件各一個。3、prop骨骼換手即max自帶的武器道具骨骼 優點是設定即為武器道具骨骼,能解決武器所需一切需求,並且被unity3D 5.0以後的版本支持,直接導出即可。
  • 【博物納新】合輯推薦—使用Unity重現經典遊戲!
    並發布了Web版本供大家在線試玩。Web版本:https://weeebox.itch.io/super-unity-bros開源庫連接:https://lab.uwa4d.com/lab/5b6620bad7f10a201ffa17703、Unity Tetris
  • 程序丨如何在Unity中創建跨平臺多人聯網遊戲?(一)
    :]需要注意的是:如果你運行的是較舊版本的Unity和較新版本的Xcode,Unity可能會在將文件導出到iOS項目後打開Xcode。如果發生這種情況,請將你的Unity版本升級到最新版本,或者簡單地打開Xcode,手動打開項目,然後從那裡繼續運行你的項目。
  • HOUSE 惠州丨@DJ UNITY 熱門電音榜單常駐者,權志龍背後的「操盤手」
    Unity在中國首創電音綜藝節目《即刻電音》中,因創造力和音樂作品的多樣性讓評委們讚嘆不已。作為節目的人氣選手,不僅是唯一一個得到三位主理人配對權的選手更在選手間票選實力榜時獲得了第一名,成為了No.1隊長。
  • Unity URP中的深度depth用法全解
    在shader graph 中如何得到片段的Eye Depth呢正交或者投影 只適用於透視+ 0.5所以我常用的是第二種方法,因為可以順便得到更多的信息 PS:NDC=, clipVec).xyz;//Ffloat3 vPos = IN.viewVec*depth01;float3 wPos = mul(_InverseView, float4(vPos, 1.0)).xyz;首先通過NDC對應的遠平面的點轉到clip space中的遠平面的點,然後用逆投影矩陣得到view space中的遠平面的點然後乘以線性深度
  • Unity中Animator做UI動畫的一些細節
    由於我之前經歷的項目,動畫使用的要麼是舊版的Legacy Animation(https://docs.unity3d.com/Manual/Animations.html),要麼是還在試用階段的Playable Graph +Animation Job(https://blogs.unity3d.com/2018/08/27/animation-c-jobs/)。
  • unity發布出來的安卓apk怎麼加密
    那麼針對unity發布的程序如何加密呢?Unity3D程序的安全問題代碼安全問題Unity3D 程序的核心程序集文件 Assembly-CSharp.dll 是標準的 .NET 文件格式,附帶了方法名、類名、類型定義等豐富的元數據信息,使用 DnSpy 等工具可以輕易地將其反編譯和篡改,代碼邏輯、類名和方法名等一覽無餘。
  • unity中使用playmaker對animator人物實現控制
    在unity中利用playmaker實現對animator人物實現控制非常簡單,只需要了解animator動畫樹的相應動作參數就能實現動畫的控制
  • Unity默認揚聲器模式設定方法教程
    想要做獨立遊戲開發者的小夥伴,大概都會使用unity進行遊戲建模,它的使用方法比較簡單,對於新手來說比較容易上手,但是有一些小地方可能會容易卡住,今天小編就來告訴大家軟體設置默認揚聲器模式的方法吧。
  • 今日直播 | Unity手遊外掛攻防之道
    主題:Unity手遊外掛攻防之道               講師:張本梁 - 網易資深安全工程師張本梁,網易資深安全工程師,從事移動遊戲安全工作超過6年林曉曉,在Unity編程教育培訓行業從事多年,具有豐富的教學培訓經驗,現擔任Unity大中華區技術講師,希望將unity技術和編程經驗傳播給眾多青少年們。
  • 實現unity級聯陰影的過渡和屏幕空間改善shadowmap漏光!
    實現unity 級聯陰影的過渡需求場景的shadowmap 精度勉強滿足場景物件的需求,但對於近距離角色的小掛件投影 衣服厚度 領子等投影精度是不足的。雖然獨佔的shadowmap可以實現,但為了不增加開銷我嘗試了用級聯陰影的最近1級做角色陰影。這樣操作很簡單不值得寫這篇內容,但這樣做需要解決2個級聯接縫問題。
  • 原創 Unity宣布發布兩個新版本程序
    Unity宣布發布兩個新版本程序,一個是Unity 的Collaborate Feature,另一個是Unity開發者驗證程序。