技術分享連載|Lua在iOS下的性能|Shader.CreateGPUProgram優化……
我們將從日常技術交流中精選若干個開發相關的問題,建議閱讀時間15分鐘,認真讀完必有收穫。如果您有任何獨到的見解或者發現也歡迎聯繫我們,一起探討。
渲染
Q:我們遊戲里要用投影儀做施法範圍顯示,圖片邊緣留幾個像素透明沒什麼問題,但是這樣導致實際範圍跟顯示不一樣,投影的圖片不留邊緣的話就會出現十字,請問是否有更好的解決方法呢?
我們用的是Unity 5.6.2版本,例子工程已經上傳至UWA問答社區。
A:紋理採樣時,紋理坐標超出[0, 1]之間的時候會取離得最近的(0或者1)上的紋理像素,即紋理邊緣的像素。這時候,如果紋理邊緣留1~2像素空白(alpha=0)則會取空白(alpha=0)像素,如果不留則是上面的情況。
建議題主解決方法有兩個:
1)紋理周邊還是留1~2像素的空白,然後在設置Projector size大小的時候根據留的像素個數對Projector size進行scale,使得scale過後的可見部分剛好是需要的size,用以修正範圍不準確問題。
2)修改Builtin Shader,在紋理採樣時判斷,如果紋理坐標超出[0, 1],則將alpha值設置為0。
該問題來自UWA問答社區,如您對該問題仍有疑問,可以轉至社區進行進一步交流。
https://answer.uwa4d.com/question/5a55c1473bc73e22933168fa
載入
Q:我在UWA的性能評測報告中看到Shader.CreateGpuProgram的耗時很高,請問這個可以優化嗎?
A:如果Shader.CreateGPUProgram僅出現1~2次,那麼其實問題不大,但如果是頻繁出現的,那麼就需要嚴格優化了。關於Shader.CreateGPUProgram的優化,主要可以分為以下幾點:
1)如果項目是通過Resources.Load來進行載入的,那麼可以通過以下兩個來實現:
將常用Shader放在「Always Included Shader」中,讓其在遊戲啟動時直接進行載入;
將常用Shader放在Shader Preloading中,以ShaderVariantCollection的形式進行載入,這種載入的好處在於可以指定動態載入的時間和載入的Keywords,不僅更加靈活方便,同時載入時間更短。
以上說的僅是完成了Shader.Parse工作,而對於Shader.CreateGPUProgram,則在載入和實例化後,在相機的視域體中直接「渲染」一幀即可,這樣會觸發Unity引擎進行CreateGPUProgram操作,從而將運行時的開銷前移,一般建議在遊戲載入或者場景切換時進行。
2)如果項目是通過Resources.Load來進行載入的,那麼直接通過AssetBundle.Load來載入Shader,完成Shader.Parse操作。後續工作同上。
注意:以上方式僅適用於Unity 5.0以後版本,並且Shader在載入後需要常駐內存中,如果後續將其卸載,那麼Shader.Parse和CreateGPUProgram操作還會再次觸發。
該問題來自UWA問答社區,如您對該問題仍有疑問,可以轉至社區進行進一步交流。
https://answer.uwa4d.com/question/58dbb737901de5f21c6569c2
渲染
Q:我看了這篇文章《用好Lua+Unity,讓性能飛起來—LuaJIT性能坑詳解》,這裡說在iOS上不支持LuaJIT, 但是可以用Interpreter模式。我看到文章里說:「必須要注意的是,ffi只有在JIT開啟下才能發揮其性能,如果是在iOS下,ffi反而會拖慢性能。所以使用的時候必須要做好快關。」
我的疑問:
1. 在IOS上開啟JIT Interpreter 也會比原生Lua快嗎?
2. 使用Interpreter模式的ffi調用C庫會比原生Lua更慢嗎?
我想在C里解析二進位協議,比如Lua調用C去readInt,readShort這樣,Lua本身不支持位運算,如果用Lua的bit庫做位運算解析性能很慢,所以想用ffi調用C來實現)。還有一種調用C的方式,就是Require C動態鏈接庫,如果iOS下的ffi性能不好的話,這個是不是最佳選擇?
A:Lua相關的協議解析庫,網上有很多,比如雲風的pbc,也基本達到產品級的可用度,可以參考或者直接用。這些庫大多也不是採取在Lua逐個欄位展開的方式,而是由C統一展開,可能會做lazy init的優化。逐欄位在Lua調用c function展開必然會把大量時間消耗在語言交互上。
ffi脫離了JIT之後性能大打折扣(10倍級別),不建議用來做大規模的協議解析。不過一般來說客戶端的協議解析不是瓶頸(前提是不能設計很大的協議,例如登錄下發的用戶數據),針對伺服器的話因為JIT是可以開啟的所以性能會高很多,另外不管是否JIT,ffi都有內存小(跟c struct一樣緊湊)的優勢。
但選擇ffi就等於綁定了LuaJIT,確實要謹慎。畢竟用LuaJIT主要是因為性能,在語言標準、可維護性、社區活躍度上都落後於Lua5.3。
該問題來自UWA問答社區,感謝招文勇提供了回答,如您對該問題仍有疑問,可以轉至社區進行進一步交流。
https://answer.uwa4d.com/question/5a537945a1fdae3c4ba3ccd9
VR 性能
Q:我們項目是VR的,製作單個建築,一個建築2000~3000面左右:
美術手動合併場景中同類型的建築,以同類為主進行合併,位置是有一定距離,會出現攝像機看到合併後的某一個建築但看不到其他建築的情況。
手動合併好後加入SimpleLOD的腳本,對每個合併好的網格進行LOD,設置為靜態讓運行時Unity再去Static Batch。運行時是靜態合批的效果,但小鎮的房屋會與非常遠的礦洞裡面的一些橫樑合併到一個大網格裡面去。
現在有如下問題想諮詢:(為方便閱讀,下文把問題和回復一併羅列)
Q:我們場景特地設置為有明顯遮擋的,按理說遮擋剔有助優化渲染,應如何提升Culling的效率?
A:其實,查看提升Culling的渲染效率還需要題主進一步通過報告查看Culling的瓶頸在哪裡。
下圖為題主這次報告的Culling具體耗時(可以在代碼效率頁面中查看),可以看到粒子系統的耗時很大。
下圖為該粒子系統Culling的具體耗時情況,建議對粒子系統的渲染數量進行控制。
Q:使用同類型模型手動合併的做法是否正確?
A:這種做法無可厚非,自己Offline合併是可以的,並不是錯誤的做法。需要注意的是,Offline合併需要注意不要把過多視域體外的物體引入計算即可。這點其實挺難做到,所以一般來說,Static Batching是一個更好的選擇。
Q:這樣的靜態合批結果是正確的么,會不會導致在礦洞看到橫樑時就把小鎮的建築都渲染出來導致渲染佔用CPU時間過高?就現在的DrawCall峰值305,三角面峰值64萬,如果針對小米5以上的手機,是否是影響CPU佔用時間的關鍵?因為小米VR是用rt實現,兩個眼睛渲染兩遍。但美術也不願意修改美術資源的精度。
A:60w的面片數確實很高,因為是雙目的,所以單屏30w渲染面片。這個對於移動設備來說,還是容易引起發熱的。建議通過Unity Editor來查看一下,是否確實存在問題2中合併過量的情況。從圖中可以看出,你的Combined Mesh應該是Static Batching來合併的,這種其實是沒問題的,即在渲染時,視域體外部的Mesh是不會參與渲染的。
Q:設計的幀更新間隔是否應該調大,例如客戶端是30ms或40ms更新一次?因為想不到其他辦法來降低CPU耗時了。
A:從下圖可以看出,無論是UWA-FrameCheckHandle-FrameFastForward還是UWA-CVRRaceScene-StepUpdate,其主要的耗時瓶頸都是這三個函數,特別是UWA-CVRRaceScene-ObstacleManager.update,對此,建議題主對這個函數進行進一步拆分,然後詳細定位它的耗時瓶頸到底在哪裡,然後才能確定優化方案。通過限幀的方式來降低耗時其實是無奈之舉,具體的時間間隔恐怕需要進行大量實驗才能找到一個適合的參數。但是,這並不能解決本質問題,本質問題還需要通過上面的方法來進行定位和解決。
該問題來自UWA問答社區,如您對該問題仍有疑問,可以轉至社區進行進一步交流。
https://answer.uwa4d.com/question/5a409c736f0cccd931e1f31f
TAG:侑虎科技 |