這是第184篇UWA技術知識分享的推送。今天我們繼續為大家精選了若干和開發、優化相關的問題,建議閱讀時間10分鐘,認真讀完必有收穫。
UWA 問答社區:answer.uwa4d.com
UWA QQ群2:793972859(原群已滿員)
本期目錄:
使用GPU Instancing屏幕花屏問題
如何優化AssetBundle大小
如何使用GPU Skinning提升性能
iOS上Shader裡tex2D採樣偏移的問題
如何管理銷毀拍攝的內置深度圖
Q:機器:魅族MX5
Unity版本:2019.1.5f1
渲染設置:OpenGL ES3,Dynamic Batching,GPU Instancing
問題表現:場景有時候會突然花屏隨機閃爍, 通過排除法發現是在渲染其中一個樹的時候導致的(樹的材質勾選了GPU Instancing,只要不渲染這個樹就不閃爍)。
嘗試改變渲染設置:僅關閉動態批處理或者GPU Instancing就都不花屏了, 兩者同時存在就會花屏。
不知道這是Unity的Bug還是魅族機器有問題。
附件:Demo工程(請戳原問答連結查看)
A:根據題主提供的信息,我們做了以下嘗試。
測試機:魅族5;對比測試機:小米6
測試場景:客戶提供
測試現象:
魅族5開啟了Dynamic和Instancing,會花屏,UBO數組長度為2,有Log報錯。
小米6和OPPO K1均不花屏,UBO數組長度均為128,無Log報錯。
魅族5在RenderDoc的數據:
當開啟GPU Instancing的時候,在Vertex Shader裡面會有2個UBO(Uniform Buffer Object)。
它們的內容分別如下:
第一個記錄的是SH函數的係數,第二個是每個Instance的變換矩陣。
在魅族5上面數組長度為2,可以理解為魅族5不支持Instance。而在小米6和OPPO K1,是支持128個Instance的,如下圖所示:
說明它們是支持Instancing的。
在移動平臺上,一個Buffer的大小上限是16KB,一個Instance需要記錄ObjectToWorld和WorldToObject的矩陣,總共是16x4x2=128 Byte,所以總的Instance上限數量是16x1024 Byte/128 Byte = 128。
從RenderDoc上的信息裡面可以看到確實是長度為128的數組,而在魅族5上面只有2。
而且在魅族5上面運行測試場景的時候,通過Logcat可以看到會有報錯的Log:
GLSL: unexpected struct parameter 'unity_Builtins2Array[1].unity_SHCArray』
GLSL:unexpected struct parameter 'unity_Builtins0Array[1].hlslcc_mtx4x4unity_WorldToObjectArray[0]'
這兩個正是Instancing需要的UBO裡面的數據。
還有一點需要說明的是,在測試場景中,使用的是239個三角形的那個Mesh,在魅族5上面同樣會花屏。不論有沒有開啟Dynamic Batching,都會有上面的報錯,所以本質上是魅族5不支持Instancing導致的。
創建了一個場景,場景中有3棵相同的樹,而且開始了Instancing,可以看到OpenGL使用的接口是glDrawElements。
在小米6上調用的接口是glDrawElementsInstannced,如下圖:
沒有對模擬器進行測試,理論上是一樣的情況。
該回答由UWA提供,歡迎大家轉至社區交流:
https://answer.uwa4d.com/question/5dce374d14ec712eefaf01ae
Q:我們AssetBundle超過3.8G了,也做了分類,但是資源太多導致總大小超過3.8G,很多玩家安裝不上。有沒有相關解決思路?
A1:隨著硬體的發展,玩家對畫質要求的提高,基本上包體越來越大是必然的,關鍵還是看大的是否合理,基本上可以順著幾個點去檢查下。
(1)首先使用UWA的資源檢測工具看下是否AssetBundle內重複資源過多,如果有,可以根據檢測結果調整,並定期提交UWA測試。
(2)配合一些資源分析的工具在編輯器內人工的工程內部資源大致過一遍,看看是否有冗餘的資源。這點還是很常見的,隨著遊戲開發版本不斷迭代,經常會出現之前做的一些東西被推翻了或者大改了(策劃或美術需要),這時候就會產生一些資源可能沒有被用到但是仍然留在工程中,如果打包時沒有相應地去掉,就會有無用的資源進版。
(3)檢查資源導入是否合理。比如:貼圖是否使用了正確的壓縮格式,動畫文件是否進行了合理的壓縮,打圖集是否合理,圖集裡是否有大量的浪費等等。
(4)如果經過檢查仍然沒有什麼進展,那只能說你們的項目真的需要這麼大的資源包,為了讓用戶能有較好的遊戲體驗,可以根據偉昊說的,規劃一部分資源在啟動後下載。這塊可以結合產品需要,做成啟動後一次性下載完或者根據遊戲進程一部分一部分下載。
感謝範君@UWA問答社區提供了回答
A2:說一個資源重複可能有些人沒注意到的點,就是同一份資源經常被改名放到不同的地方被不同的資源引用,經過實際檢測,這個重複量大過了我的預測,大家可以自測一下自己的項目。
感謝noah@UWA問答社區提供了回答,歡迎大家轉至社區交流:
https://answer.uwa4d.com/question/5dc0ea627307ec2f0f99a0e7
Q:我們的遊戲參考了UWA Blog上的那篇《GPU Skinning加速骨骼動畫》,發現真機環境幀數提升不是很明顯,連上Profiler發現開啟/關閉GPU Skinning總批次居然沒啥變化,SetPass Calls倒是變化明顯,在編輯器切到Android平臺我測試過總批次會提升100+,有點奇怪為什麼到了Android真機裡沒什麼變化?
PS:原來角色動畫Animator + SkinMeshRenderer,使用GPU Skinning後換成了MeshRenderer同時把Animator勾去掉的。
A:可汗文章中單純的GPU Skinning並不是去降低渲染Draw Call的,只有結合GPU Instancing才會降低Draw Call。GPU Skinning最主要降低的是Animators.Update和MeshSkinning.Update的耗時,如果在使用後,你發現這兩個值沒有變化或者優化不大,那麼十有八九是用法不對。
下圖是我們在UWA DAY 2019 《如何根據UWA制定技術選型》 (https://edu.uwa4d.com/course-intro/1/95)時做的具體的性能測試,裡面講解了多種不同方法所能達到的收益和限制,題主有興趣可以詳細查看。
同時,我們將之前做過的一些定量測試放在這裡,希望大家對於GPU Skinning帶來的性能改變有更為定量的理解。
——————————————
通過GPU Skinning方式
該方法是完全摒棄Unity引擎的MeshSkinning和Animator模塊,自行對蒙皮網格進行採樣,將骨骼結點的矩陣信息以紋理的方式進行儲存,然後在GPU中完成頂點計算並直接進行渲染。優點在於極大地降低SkinnedMesh.Update和Animator.Update的CPU佔用。將骨骼結點信息通過紋理來進行儲存,因而數據量較之方案兒會大為降低。
開源庫下載連結:
https://lab.uwa4d.com/lab/5bc6f85504617c5805d4eb0a
測試場景:
創建相同案例,場景模型數量分別為50和200,各自測試1000幀,播放Walk動畫。
結果:
該方案測試效率如下圖所示,除Camera.Render外,MeshSkinning.Update和Animator.Update已經消失,但增加了GPUSkinning.Start和GPUSkinning.Update函數。通過分析可知,在紅米Note2設備上,開啟多線程渲染功能,測試幀數總計1000幀,50個模型的CPU平均耗時0.6ms;200個模型的CPU平均耗時1.6ms。
圖1:使用GPU Skinning方案後,紅米Note2上的Top10 CPU佔用情況
使用GPU Skinning方案後,紅米Note2上的Camera.Render耗時情況。
圖2:50個模型
圖3:200個模型
使用GPU Skinning方案後,紅米Note2上的50個模型時的Camera.Render耗時情況。
圖4
同時,GPU Skinning.Start和GPU Skinning.Update耗時在遊戲運行過程中很小,如圖5所示。
圖5:GPU Skinning.Start和GPU Skinning.Update耗時在遊戲運行過程中的CPU耗時
總結:
(1)該方案可以大幅降低Animators.Update和MeshSkinning.Update的CPU耗時,同時內存佔用較小小。以r_gunman模型為例,其所有動畫文件時長8秒,如果採樣率為30fps時,通過紋理來進行記錄,只需要128x128的紋理即可得到更為精細的動畫數據;
(2)該方案對於GPU的壓力更大,需要研發團隊對GPU方面的壓力進行進一步權衡。
該回答由UWA提供,歡迎大家轉至社區交流:
https://answer.uwa4d.com/question/5dccb9137307ec2f0f99a1ca
Q:我是用世界坐標去採樣一張Filter Mode為Point的紋理,PC和Android真機上採樣是正確的,Mac和iPhone7/7Plus上採樣結果產生了偏移。
PC上的截圖(分別為輸出UV和輸出採樣結果):
Mac上的截圖:
注意看網格線的對比,Mac上的採樣圖出現了偏移,但UV是正確的(為了方便觀察,UV進行了縮放,採樣是用未縮放的UV)。
有人碰到過類似的問題嗎?怎麼處理的?
A1:問題根源並沒有找到,目前採用了一個取巧的方案繞過該問題。
原本需求就是用世界坐標採樣一張Filter Mode 為Point 的紋理,因此直接在Shader中棋盤化採樣點,偽代碼:
float2 uv = floor(worldPos.xz / rectSize) / texSize.xy + 1.0 / (3.0 * texSize.xy);(rectSize 為棋盤格子的大小;texSize 為採樣圖的大小)
最後加的那個值(1.0 / (3.0 * texSize.xy))是測試發現如果沒有該值,PC採樣結果和Mac下會有偏差,因此加上了三分之一個單位的偏移。
感謝珂@UWA問答社區提供了回答
A2:應該和這個問題(https://forum.unity.com/threads/half-a-pixel-visual-offset-between-directx-and-opengl.73492/)類似吧,不過half pixel offset好像是bilinear filter才會有的,point可能也有不同平臺採樣結果不一樣的問題。
https://docs.microsoft.com/zh-cn/windows/win32/direct3d9/nearest-point-sampling
https://docs.microsoft.com/zh-cn/windows/win32/direct3d9/bilinear-texture-filtering?redirectedfrom=MSDN
可以看看上面兩個連結有沒有幫助。
感謝noah@UWA問答社區提供了回答,歡迎大家轉至社區交流:
https://answer.uwa4d.com/question/5dd3539130e0542f29cd5b96
Q:在同一個場景我先後使用不同的攝像機不同的角度拍攝深度,那麼應該得到多張不同的深度圖,我想了解Unity對這多張深度圖是怎麼管理的?比如:我只保留其中一張,其它的深度圖都不需要了,我該怎麼處理?
A:Unity默認渲染管線是多相機共享RenderTexture的方式,即多個相機逐一使用公共的CameraDepthRenderTexture繪製,每個相機繪製完是否Clear,取決於你下一個相機的ClearMode。至於RenderTexture的內存分配和管理,與你所有的相機是否開啟繪製深度有關,全部關掉繪製深度底層會釋放掉RenderTexture。如果要保留一張給下一幀使用,那麼建議拷貝出來,這樣不會影響下一幀其它相機使用。
感謝Wangtao@UWA問答社區提供了回答,歡迎大家轉至社區交流:
https://answer.uwa4d.com/question/5dce59497307ec2f0f99a224
封面圖來源:Unity Gpu Instancing
GPU Instancing + Boids + Animation Texture Baker
https://lab.uwa4d.com/lab/5b9fc1f902004fb659ba16de
今天的分享就到這裡。當然,生有涯而知無涯。在漫漫的開發周期中,您看到的這些問題也許都只是冰山一角,我們早已在UWA問答網站上準備了更多的技術話題等你一起來探索和分享。歡迎熱愛進步的你加入,也許你的方法恰能解別人的燃眉之急;而他山之「石」,也能攻你之「玉」。
官網:www.uwa4d.com
官方技術博客:blog.uwa4d.com
官方問答社區:answer.uwa4d.com
UWA學堂:edu.uwa4d.com
官方技術QQ群:793972859(原群已滿員)
(長按識別二維碼進入UWA問答)
自動化測試已上線!
近期精彩回顧
【厚積薄發】LWRP下代碼動態更改陰影生成距離
【博物納新】基於DOTS的UI解決方案
【學堂上新】成熟的Unity熱更新以及版本管理的解決方案
【厚積薄發】一種Shader變體收集和打包編譯優化的思路