軒轅傳奇場景優化筆記
最近在做一些優化相關的內容,每個版本的性能review。順便翻了翻以前的優化筆記,找到了一篇寫的還算正式的筆記。13年的工作生涯大半時間都在做渲染和優化相關的內容,優化這東西真的是扁鵲三兄弟的故事,只有痛了才知道它的重要性,而當它不是問題的時候卻很少有人會注意到它。
目的和需求:
- 客戶端安裝包過大,玩家下載不方便,希望能減少安裝包容量。
- 由於客戶端Camera拉平,希望能顯示更遠更大的模型,同時希望模型能平滑出現,提升客戶端表現。
- 由於Camera拉平導致客戶端效率有一定程度的下降,希望能提高效率,提高幀數。
- LightMap,陰影等存在模糊配置不合理的問題,希望顯示效果能更好。
- 客戶端有一些奇怪的花屏渲染錯誤,隨機出現,猜測是顯存碎片較多。
目標:
- 減少10%客戶端容量;
- 在保證效率的前提下提升客戶端表現,看的更遠;
- 在顯示範圍更大,顯示效果更好,Camera拉平的前提下,提高渲染效率,維持甚至超過之前版本的幀數;
- 減少內存顯存碎片,優化Shader,消除渲染錯誤,加入限幀等,避免一些花屏死機等渲染錯誤;
容量方面優化:
針對客戶端較大的問題,我們首先分析了軒轅的資源文件大小。其中比重最大的是場景數據,未壓縮前有2G,壓縮後有425M,共85張地圖並且策劃還會添加新的地圖。
場景數據是由地圖編輯器導出,主要包括地形的模型數據,水的索引類型數據高度圖,草的模型數據,LightMap數據等。通過分析發現地形數據,水的高度圖,草的數據,LighMap比較大,占絕大部分。
地形數據
我們的地形數據由Chunk組成,每個Chunk有16x16個網格,每格代表2米,每個頂點有頂點索引(4個byte),頂點坐標(12個Byte),法線(12個Byte),頂點色(16個byte),2套UV坐標(BaseMap和LightMap16個Byte),具體結構如下。
- 因為每個Chunk的大小規格都是一樣的,所以每個Chunk的頂點索引都是固定的,這樣我們可以在客戶端計算。客戶端有全局唯一一份地形頂點索引,這樣編輯器導出數據每個頂點可以節省4個byte。
- 首先頂點的x,y坐標可以通過Chunk在地形中的偏移和當前地形網格在當前Chunk所有網格中的索引計算出來,這樣每個頂點可以節省12個byte,為了減輕CPU的負擔我們在GPU中計算,計算方式如下:
WorldPos.xy= g_fOffset.xy * CHUNK_SIZE + _f3Position.xy * GRID_SIZE;
WorldPos.z= _f3Position.z;
(_f3Position.xy是當前地形網格在當前Chunk所有網格中的索引,g_fOffset是當前Chunk在整個場景中的索引,CHUNK_SIZE是每個chunk的大小,GRID_SIZE是每個網格的大小。)
- 然後Chunk的UV也是固定的。我們的Chunk有兩套UV,第一套UV用於採樣5張基本紋理,1張高光紋理;第二套紋理用於採樣1張混合紋理,1張陰影AO紋理,1張點光紋理。Chunk的2套UV中第二套UV固定是0-1,第一套UV存在一個循環次數係數(循環幾次由美術在編輯器指定)也可以計算的出,這樣每個頂點可以節省16個byte,計算方式如下:
_f3OutTex2.xy= _f3Position.xy / 16.0f;
_f3OutTex1.xy= _f3OutTex2.xy * g_fOffset.z;
(其中g_fOffset.z是循環係數。)
- 其次法線和頂點色我們可以通過各使用一個DWORD來壓縮。頂點色可以將顏色的RGBA各一個float壓縮成0-255的一個byte然後拼成一個DWORD以D3DCOLOR的格式直接傳給顯卡(D3D會自動轉換成float),這樣每個頂點可以節省8+12共20個byte。法線也同樣可以壓縮成一個DWORD,不過因為法線會有負數需要轉換一下。為了減輕CPU負擔,避免客戶端載入時遍歷所有頂點我們也在GPU中計算,計算方式如下:
編輯器CPU壓縮:
bytebX = (kNormal.x - (-1.0f)) / 2.0f *255 ;
bytebY = (kNormal.y - (-1.0f)) / 2.0f *255 ;
bytebZ = (kNormal.z - (-1.0f)) / 2.0f *255 ;
客戶端GPU解壓:
Normal= Normal / 255 * 2.0f - 1.0f;
- 最後因為GameBryo的常規格式不能指定DWORD作為頂點色(必須是float,GB最終會遍歷每個頂點轉換成DWORD),所以使用了多流的方式,將頂點坐標放在一個流,頂點色和法線放在另一個流中傳入GPU。
草
草的做法是每個地形的網格上對應兩個交叉的四邊形面片,使用多流,GPU計算頂點,減小容量;
水
水的相關數據比較大的部分是水的高度圖,在客戶端用於計算水花的高度。軒轅的水是對應每個地表格子,但是因為水主要為了減小數據大小。不導出水的高度圖,只導出每片水的高度;
LightMap縮小
最終我們採用了以下幾種優化方案:
- 地形方面,主要是減小地形數據大小。不導出地形頂點索引,x,y坐標,UV坐標,通過Shader計算;使用 dword導出法線和顏色,使用多流的方式渲染地形;
- 草,主要為了減小數據大小。編輯器不導出模型數據,客戶端計算頂點,頂點索引,UV等;
- 水,不導出高度圖,減小容量;
- 減小地形和特殊地表的LightMap,ShadowMap的大小;
測試結果:
因為在我們優化後的地圖又新增增了25張,取新老都有的85張地圖作比較:
優化後減少了130M,約30.6%,如果考慮新增的25張地圖預計實際減少170M。再次優化後比第一次優化又縮小了10%,估計壓縮後會再節省20M左右(因為全部烘一遍時間太長陳悅使用全黑貼圖,壓縮後的數據屬於估計。),總計減少150M,約35.3%。
渲染方面
首先我們為了使遊戲視角拉平的情況下表現較好的效果,做出了如下調整:
- 擴大地形的顯示範圍從5x5個Chunk改為最高配情況下地形地表11x11,物件7x7;
- 同時加入了物件的半透明漸變出現和消失;
- 地形地表和模型分開載入,優先保證地形地表載入,同時優先保證視野範圍內的Chunk優先載入;
- 為了Shader擴展調試方便,我們使用ubershader自動生成shader,去掉冗餘運算。
通過使用GPA,Pix等分析軟體再次分析Camera拉平顯示範圍擴大後的場景,最後發現:
- 我們的Shader還是比較複雜,特別是高配置Shader;
- 遊戲Shader還有優化餘地,例如霧效,例如物件,雖然有的物件不受點光影響但是還是使用帶點光貼圖的Shader等;
- 從前面LightMap合併前的PIX分析結果可以看出,物件雖然已經分類渲染(例如地形一批,場景物件一批,人物一批等),但是由於不斷增加的需求,同一類物件渲染的Shader也有很多變化,例如同一批渲染的場景物件,也區分是否有Glow是否實時點光等等,這樣就導致會反覆設置渲染狀態。;
- 渲染狀態切換次數太多,通過GPA和PIX分析,發現目前客戶端渲染狀態切換次數太多。平均渲染一個物件可能要設置7.6次渲染狀態,其中大部分地形和物件有一張保存點光和點光陰影的貼圖,一張保存陰影(R通道)和AO(G通道)的貼圖,渲染時要設置兩次,並且每張貼圖都十分小,大部分只有16x16個像素。如下圖:
- 文字和UI的效率太低,目前每幀創建NiScreenElement每幀需要多次Lock;
- 存在一些冗餘的渲染;
- 陰影比較耗時;
最終我們採用了以下幾種優化方案:
- 霧效shader近一步優化,,優化計算深度shader,水的shader,同時利用切換shader的方式避免一個shader計算量太大(例如有半透明和沒有半透使用不同shader,如果沒有半透明時不需要計算);
- 針對不同的圖形渲染級別準備不同的渲染Shader,特別針對超低配置的顯卡實現了簡單Shader,去掉了實時光照、實時陰影、AO、高光、霧等,簡化了地表紋理混合;
- 材質Lod,主要是提升渲染效率。對較遠範圍的模型,地形和水等使用低級別的材質;
- 修改了一些反覆切換shaderbug,加入了shader排序;
- 修改文字的創建,不在每幀創建NiScreenElement,利用緩衝避免頻繁lock,渲染時合併到一個批次渲染,消除Lock VB IB的等待;
- UI的使用畫布,只在髒了後更新;
- 去掉GPA分析出的一些冗餘渲染,例如弒神,馬等UI即使不可見也創建RT並渲染等;
- 陰影優化,減小陰影圖大小,修改陰影差值方式,去掉了陰影第四級別這個基本看不出效果又比較費的功能;
- 將LightMap和ShadowMap合併,主要為了提高效率,減少設置貼圖切換次數,減少顯存碎片,數據大小會增大1%左右;
- 關閉地形實時陰影,主要為了提高效率,開啟特殊地表實時陰影,為了美術效果。
- LightMap和ShadowMap解析度調整,主要為了提升顯示效果同時縮小客戶端容量;
- 主次物件區分,主要是提升客戶端表現和提高客戶端效率。對物件劃分主次,主要物件和特殊地表地形顯示載入範圍一樣,次要物件顯示較小範圍並有半透明漸變;
- 我們加入了Shader排序功能。通過GPA分析發現加入Shader排序可以減少大概200次渲染狀態切換
測試結果:
性能方面,在高端電腦(CPU:i72600;顯卡:5800;內存8G),中端電腦(主要測試組測試,在各種中端電腦上測試,反饋是優化前優化後幀數內存基本沒變化),低端電腦(CPU:P42.0;顯卡:5200;內存1G)幀數都沒有明顯變化,其中高端和低端的一些數據如下,紅色表示更糟糕,有些變化是因為地圖本身美術修改過了:
高端電腦(幀數分渲染幀和邏輯幀,括弧內是邏輯幀!):
低端電腦,因為是超低配置所以LightMap合併,多流,材質LOD都不需要,主要物件範圍和一般物件也是一樣的,僅次要物件顯示範圍變小了,優化主要表現在地形數據的減少上:
需要說明的是軒轅城優化前後地圖本身變化太大,所得數據不能代表實際結果。低配和高配測試在不同電腦上,測試位置略有不同。
使用GPA分析殤州城:
- 優化前,796個DP,5976次狀態切換,12次VBLock,平局每個dp切換7.5次渲染狀態。
- 優化後,772個DP,5883次狀態切換,12次VBLock,平局每個dp切換7.6次渲染狀態。
可見優化後渲染的物件少了,主要是主次物件的原因,但是狀態切換並沒有減少多少。通過Pix分析,合併貼圖後有大量32x32的小貼圖再加上渲染順序的原因減少的設置貼圖狀態次數減少不多,Shadere中對Lightmap等貼圖設置MipFilter為Line但實際上沒有GB會自動設置成None,因為使用多流會多設置一次流數據,增大了狀態切換次數。同時因為加入材質LOD,近景半透明漸變等,使Shader種類多了但是由沒有排序,也增大了狀態切@換次數。
再次優化後(合併Ligmap和ShadowMap到一起,修改Shader貼圖採樣方式,加入Shader排序,重新劃分主次物件,限制最小LightMao為32x32最大為256x256地圖減小了近10M),822個DP(DP數增多是因為策劃新加入一些表現ID物件,同時加入了特殊地表的實時陰影,並且此屏沒有近景物件),5631次狀態切換,12次VBLock,平局每個dp切換6.8次渲染狀態減小了約9%,總計節省狀態切換約558次。
使用Pix分析第二次優化版本:
通過頂點格式,可以看出優化後地形使用了多流,近景地形的渲染狀態切換較多;草,玩家的渲染狀態相對較少;場景物件在合併LightMap和ShadowMap後減少了切換次數。下圖是比較。
優化前場景物件:
優化後場景物件:
可見優化後最好情況下物件渲染少了兩次設置texture的渲染狀態,但是因為部分物件沒有點光貼圖,並且合併貼圖的順序不一定完全符合渲染順序,並沒有減少模型數*2那麼多次,一屏三百多個物件總計節省了418次左右渲染狀態切換,地形多流增加幾十次狀態切換,shader排序減少200多次狀態排序,所以最終減少558次狀態切換。
邏輯方面:
通過使用內部的profile分析工具和Intel的ParallelAmplifier,分析,發現在多人情況下玩家的更新渲染占很大比重,其中動作更新占很大比重。
- 更新頻率優化,降低更新頻率,限制幀數為40幀;
- 對於玩家的更新加入範圍判斷,對較遠的(超出屏幕範圍)的玩家不進行更新和顯示;
- 在大量玩家的情況下加入多線程骨骼更新;
- 拾取優化,加入包圍盒剔除,並對Camera碰撞的拾取做了優化;
- 排序優化,去掉半透明排序節約時間,手工調整渲染順序利用,利用反畫家演算法進行earlyz的優化;
結論:
- 此次客戶端優化大幅減少了編輯器導出數據大小(大約30%);
- 客戶端可視效果比之前更好可以看的更遠。
- 在更好效果的基礎上,幀數和內存都與優化前基本保持。
- 大幅減少了狀態切換次數和顯存碎片,消除了一些渲染錯誤的問題
- 優化資源應該儘可能的考慮周全,並做大量測試減少反覆性,特別在遊戲後期,每一次涉及地圖格式的改動都會引起較大的美術工作量和測試工作量
此次客戶端優化大幅減少了編輯器導出數據大小(大約30%),減少了渲染狀態切換次數(9%),並且可視效果比之前更好可以看見更遠的較大的模型,在此基礎上客戶端幀數和內存都基本保持,高配下幀數還略有提高內存略有減小。總體來說達到了預先期望的在不影響性能的前提下減小安裝包容量的需求。
推薦閱讀: