移動平臺Unity3D 應用性能優化

做者:陳星百,騰訊移動客戶端開發 工程師html

商業轉載請聯繫騰訊WeTest得到受權,非商業轉載請註明出處。編程

原文連接:http://wetest.qq.com/lab/view/315.html數組

WeTest 導讀

作了大概半年多VR應用了,VR因爲雙眼double渲染的緣由,對性能的優化要求比較高,在項目的進展過程當中,總結了一些關於移動平臺上Unity3D的性能優化經驗,供分享。緩存


1、移動平臺硬件架構

移動平臺不管是Android 仍是 IOS 用的都是統一內存架構,GPU和CPU共享一個物理內存,一般咱們有「顯存」和「內存」兩種叫法,能夠認爲是這塊物理內存的全部者不一樣,當這段映射到cpu,就是一般意義上的內存;當映射到gpu,就是一般意義上的顯存。而且同一段物理內存同一時刻只會映射到一個device。性能優化

圖片描述

即便是在同一物理內存上 ,以前的openGL ES規範中CPU和GPU之間的內存是不能共享的,vertex和texture的buffer是須要拷貝的。後面出來的vulkan 與IOS的metal 能夠共享內存。服務器

瞭解了移動平臺的硬件架構,就知道了 1)CPU 2) 帶寬 3) GPU 4) 內存 都有可能成爲移動平臺3D應用性能瓶頸。網絡

2、移動平臺3D應用的畫面渲染過程

一、CPU經過調用繪製命令(稱爲一次Draw Call)來告訴GPU開始進行一個渲染過程的。一個Draw Call命令會指向本次繪製須要渲染的信息,這些信息包括:頂點數據、紋理數據、shader參數(光照模型、法線方向、光照方向等)等,簡單地說就 畫什麼,用什麼畫,怎麼畫。
圖片描述多線程

二、GPU接收到Draw Call命令以後就會開始進行一次單元渲染,關於GPU的單元渲染的過程是這樣的(簡單示意圖):
圖片描述架構

1)從顯存中取出拷貝的頂點數據和光照模型。app

2)經過頂點處理器(Vertex Processor)對頂點數據進行一系列的變換和光照處理,包括裁剪處理。tips: 簡單的想一想,遊戲中的各個物體的座標都是參照遊戲中的世界座標系的,而實際顯示的畫面是玩家視角或者攝像機視角,這中間就有許多座標系的轉換。這些活就須要頂點處理器來作,最終咱們獲得了咱們所須要視角的畫面。

3)到這一步,畫面還只是一些多邊形,而實際顯示在屏幕上的是一個個像素,這裏就須要(光柵處理器)Rasterizer進行光柵化(Rasterization),從而將畫面變成一個像素圖,把全部的頂點對應到一個一個的像素位置。

4)對這些像素進行上色,經過片元處理器(Fragment Processor)中的像素着色器(Pixel Shader)按照shader光照模型,根據紋理對應位置顏色,計算元顏色,再通過深度計算、透明測試計算出每一個像素的最終顏色。

5)把結果輸出到圖像緩存中,所有完成後拿去顯示。

3、Unity3D應用性能優化之CPU

CPU的優化很是重要,CPU的表現直接決定了VR應用的幀率,應用的耗電量,發熱量。咱們來看看相比於普通的app,VR應用的CPU都承擔了什麼責任:a、業務邏輯 b、網絡通訊 c、I/O操做 d、drawcall e、physic邏輯 f、GC內存回收 g、垂直同步等待。

業務邏輯、網絡通訊、I/O操做

這一塊的優化和普通的app差很少。

關於業務邏輯:有些不一樣的是Unity腳本中有一類update方法(Update、FixedUpdate、OnGUI等),這一類方法是在每幀刷新的時候調用的,是比較影響每幀耗時的,爲了嚴格控制這一部分的執行時間,須要注意的如下幾點:
a、儘可能不要再Update函數中作複雜計算,若有須要,能夠隔N幀計算一次,對於純數學計算,能夠開闢新線程來計算(Unity 爲何通常避免使用多線程, 實際上大多數遊戲引擎也都是單線程的, 由於大多數遊戲引擎是主循環結構, 邏輯更新和畫面更新的時間點要求有肯定性, 若是在邏輯更新和畫面更新中引入多線程, 就須要作同步而這加大了遊戲的開發難度。UnityEngine絕大多數類是不支持子線程的,因此通常只有純數學計算纔會用到子線程去計算。)

b、關閉全部在update類中執行log的打印操做(Unity中一次log打印有時長達7ms,Profiler數據)。

c、不在update類方法調用Getcomponent、SendMessage、FindWithTag這幾個耗時較長的方法。

d、不在update類方法中使用臨時變量。

關於網絡通訊、I/O操做:這些普通app的優化和注意點沒有什麼很大區別,有一點是,Unity工程中使用了資源動態加載,有些資源是保存在服務器端的,在有必要的時候纔會經過網絡load下去加載。這個資源動態加載須要注意一個問題:因爲網絡通訊過程,CPU老是處於等待的狀態,通常資源下載是多線程同時操做,爲了儘快上屏顯示資源(在這個工程中是一些圖片和英雄的3D模型),可是資源有多是在同一個幀週期中下載完畢的,若是直接加載的話,可能會出現Camera瞬時渲染過多三角形面,形成渲染時間(Camera.Render()函數執行時間)過長,,卡頓的現象。因此這裏要注意,網絡下載能夠多線程多任務同時下載,可是在Unity主線程,要避免出現同時加載大型模型和大紋理的狀況,最好使用隊列的方式,保證一幀只渲染一個3D模型。

關於GC

爲何要把GC放在CPU這一部分?雖然GC是用來處理內存回收的,可是卻增長了CPU的開銷(GC一次開銷可長可短,有時長達100ms)。所以對於GC的優化目標就是儘可能少的觸發GC。

圖片描述

首先咱們要知道所謂的GC是Mono運行時的機制,而非Unity3D遊戲引擎的機制,因此GC也主要是針對Mono的對象來講的,而它管理的也是Mono的託管堆。 明白了這一點,你也就明白了GC不是用來處理引擎的Assets(貼圖,音效,模型等等)的內存釋放的,由於U3D引擎也有本身的內存堆而不是和Mono一塊兒使用所謂的託管堆。其次咱們還要清楚什麼東西會被分配到託管堆上?對,就是引用類型。引用類型包括:用戶自定義的類,接口,委託,數組,字符串,Object.而值類型包括:幾種基本數據類型(如:int,float,bool等),結構體,枚舉,空類型。因此GC的優化也就是代碼的優化。

那麼GC何時會觸發呢?兩種狀況:
a、當咱們的堆的內存不足時,會自動調用GC來回收內存。
b、手動的調用GC,用System.GC.Collect(),通常狀況下,不建議手動去手動進行內存回收,由於容易出現問題。

檢查整個工程代碼,關於減小GC這一方面的優化經驗總結大概以下:
一、字符串鏈接的處理。由於將兩個字符串鏈接的過程,實際上是生成一個新的字符串的過程。而以前的舊的字符串天然而然就成爲了垃圾。而做爲引用類型的字符串,其空間是在堆上分配的,被棄置的舊的字符串的空間會被GC當作垃圾回收,可使用StringBuilder來解決(注意:C#沒有StringBuffer,Java裏纔有!!String 在進行運算時(如賦值、拼接等)會產生一個新的實例,而 StringBuilder 則不會。因此在大量字符串拼接或頻繁對某一字符串進行操做時最好使用 StringBuilder,不要使用 String)。

二、儘可能不要使用foreach,而是使用for。foreach其實會涉及到迭代器的使用,而據傳說每一次循環所產生的迭代器會帶來24 Bytes的垃圾。那麼循環10次就是240Bytes。

三、不要直接訪問gameobject的tag屬性。好比if (go.tag ==「human」)最好換成if (go.CompareTag (「human」))。由於訪問物體的tag屬性(每次Object.name也會分配39B的堆內存.)會在堆上額外的分配空間。若是在循環中這麼處理,留下的垃圾就可想而知了。

四、不要實例化(Instantiate)和(Destroy)對象,事先建好對象池,以實現空間的重複利用。

五、在某些可能的狀況下,可使用結構(struct)來代替類(class)。這是由於,結構變量主要存放在棧區而非堆區。由於棧的分配較快,而且不調用垃圾回收操做,因此當結構變量比較小時能夠提高程序的運行性能。可是當結構體較大時,雖然它仍可避免分配/回收的開銷,而它因爲"傳值"操做也會致使單獨的開銷,實際上它可能比等效對象類的效率還要低。因此要注意選擇。

六、場景切換時,能夠主動進行垃圾回收(調用System.GC.Collect()),從而及時去除遊戲中已經沒必要要地內存佔用。

Draw Call 的優化

前面說過了,DrawCall是CPU調用底層圖形接口的操做。好比有上千個物體,每個的渲染都須要去調用一次底層接口,而每一次的調用CPU都須要作不少工做,那麼CPU必然不堪重負。可是對於GPU來講,圖形處理的工做量是同樣的。

咱們先來看看Draw Call對CPU的消耗大概是一個什麼級別的量:
NVIDIA 在 GDC 曾提出,25K batchs/sec 會吃滿 1GHz 的 CPU,100%的使用率。有一個公式能夠和清楚得計算出在給定的CPU資源 與 幀率的狀況下,最多能有多少個DrawCall。
DrawCall_Num = 25K CPU_Frame CPU_Percentage / Framerate。
DrawCall_Num : DrawCall數量
CPU_Frame : CPU 工做頻率(GHz單位)
CPU_Percentage:CPU 分配在DrawCall這件事情上的時間率(百分比)

Framerate:但願的遊戲幀率

好比說咱們使用一個高通820,工做頻率在2GHz上,分配10%的CPU時間給DrawCall上,而且咱們VR要求60幀,那麼一幀最多能有83個DrawCall(因爲雙camera的存在,單眼DrawCall只能保證在41個之內)。其實,google官方的建議是單眼DrawCall很少於50個。

因此對DrawCall的優化,主要就是爲了儘可能解放CPU在調用圖形接口上的開銷。因此針對drawcall咱們主要的思路就是每一個物體儘可能減小渲染次數,多個物體最好一塊兒渲染。那麼DrawCall次數的優化有哪些方案呢?

DC Batching(DC批處理)
batch即批處理,DrawCall batching即DC的批處理,即把屢次DrawCall合併成一次DrawCall的方案。

Dynamic Batching 動態批處理
Unity引擎對於使用相同材質的物體會自動進行批處理,相同材質意味着shader徹底同樣,這一部分主要是要注意那些破壞這一特性的人爲因素,好比說:
一、批處理動態物體須要在每一個頂點上進行必定的開銷,因此動態批處理僅支持小於900頂點的網格物體,若是你的着色器使用頂點位置,法線和UV值三種屬性,那麼你只能批處理300頂點如下的物體(若是在這基礎上還使用了UV2,則只能批處理180頂點如下的物體);請注意:屬性數量的限制可能會在未來進行改變。

二、使用不一樣的縮放比例的物體,unity將沒法對這些物體進行批處理。好比(1,1,1)和(1,2,2)就不會動態批處理,可是(1,1,1)和(2,2,2)會動態批處理。

三、擁有lightmap的物體含有額外(隱藏)的材質屬性,好比:lightmap的偏移和縮放係數等。因此,擁有lightmap的物體將不會進行批處理(除非他們指向lightmap的同一部分)。接受實時陰影的物體也不會批處理。

四、多通道的shader會中斷批處理操做(爲了達到特殊的渲染目的,可能某個物體要多遍渲染.這是就要多個通道)。

五、在腳本中動態地指定了物體的材質,也不會進行批處理。

Static Batching 靜態批處理
動態批處理雖然是自動的,可是限制很是多,不當心就會打破批處理,因此unity在專業版中還提供了靜態批處理,靜態批處理要求是想批處理的物體必定是static的,靜態的,不會改變位置和旋轉角度以及縮放的,且必須材質一致。其原理是把物體的網格進行合併,變成一個靜態的更大的網格物體,再使用一個統一的材質進行渲染。

知道了它的原理,它的某些坑就比較清晰了:
一、在一個平行光、環境光下,沒有問題,可是若是你使用了多個平行光,點光源,聚光燈這種複雜的光源去照射物體,那麼靜態批處理就會被打斷。(項目中就遇到過,由於兩邊有兩排英雄模型,因此場景中使用了兩個不一樣平行光,場景中勾選的static物體並無被合併drawcall,通過一番折磨才找到緣由)。

二、若是靜態批處理前有一些物體共享了相同的網格,那麼每個物體都會有一個該網格的複製品(原本unity只會保留一份,可是靜態批處理會生成新的一個大網格,因此會保留全部物體的網格,最後合併),即一個網格會變成多個網格被髮送給GPU。這樣會形成內存的使用變大,須要注意這個問題,可是通常場景中使用相同網格的物體會比較少。

三、對於那些shader相同,紋理不一樣致使的不一樣材質沒法進行批處理的物體(好比項目中的場景環境,基座,地面,其實都使用了unity自帶的standard shader)能夠經過紋理合並的方法來使得它們能夠被靜態批處理。這就引起了下面的事情:
BUS總線帶寬
CPU完成一次DrawCall,除了須要發一個DrawCall的命令以外,還須要把內存中頂點數據、紋理貼圖、shader參數經過bus總線拷貝到內存分配給GPU的顯存之中,注意這是拷貝,不是指針傳遞,速度不快。若是一次drawcall傳遞的數據過大,帶寬成爲了瓶頸,那就會大大影響效率(其它的DrawCall沒法出發,GPU又處於閒置)。這種狀況最有可能出如今爲了減小DrawCall,瘋狂的合併紋理上。在項目中,UI的DrawCall調用佔了很大一部分,也會最難優化的,爲了減小drawcall ,咱們把UI模塊的靜態部分(一些UI的底板,背景等不會發生變化的)所有合併成了一個紋理,最後致使了DrawCall降低了,可是幀率卻也降低了,內存使用也增長了,緣由就是這個。在項目中,不會同時出現的元素不要打包到一塊兒,保證單張合併紋理不大於10241024通常就不會有問題了(王者榮耀最大紋理限制在了256256)。

DrawCall的優化大概就是這些,優化的目標實際上是往一個目標上靠,cpu的DrawCall命令剛恰好能被GPU消化,不要讓CPU等待(帶寬限制),也不要讓GPU閒置。若是即便作到了這個,應用幀率仍是上不去,那麼就只能去削減場景,作有損優化了。

Physics

Unity內置NVIDIA PhysX物理引擎,來模擬物理世界的一些效果,好比說重力、阻力、彈性、碰撞這些,其中使用了一些內置的組件來實現這些模擬,用的比較多的如:剛體(Rigidbody) 各類碰撞器(Collider) 恆力 (Constant Force) 物理材質(Physic Material)鉸鏈關節(Hinge Joint)彈簧關節(Spring Joint)。

unity除了提供了一些重要的組件以外,在unity腳本中的生命週期中提供了一個專門爲物理計算的刷新方法:
FixedUpdate()。FixedUpdate跟Update的區別在於,這兩個函數處於不一樣的「幀循環」中,FixedUpdate處於Physics循環中,而Update不是。因此這兩個函數的使用也有了不一樣。Update的執行受場景GameObject的渲染的影響,三角形的數量越多,渲染所須要的時間也就越長。FixedUpate的執行則不受這些影響。因此,Update每一個渲染幀之間的間隔是不相等的,而Fixedupdate在每一個渲染幀之間的時間間隔是相等的。因爲關係到物理模擬,因此通常涉及到物理組件,都須要放在Fixedupdate中進行計算。那麼關於physics,通常的優化手段都有哪些呢?下面是一些經驗及總結:

一、將物理模擬時間步間隔設置到合適的大小。 Fixed Timestep是和物理計算有關的,因此若計算的頻率過高,天然會影響到CPU的開銷。同時,若計算頻率達不到軟件設計時的要求,有會影響到功能的實現,因此如何抉擇須要具體分析,選擇一個合適的值,通常大於16ms,小於30ms。能夠經過Edit->Project Settings->Time來改變這個值。

二、謹慎使用網格碰撞器(Mesh Collider),過於消耗性能,通常使用更簡單的碰撞器,或者使用基本幾何碰撞器合併的組合碰撞器。在這個項目中,把全部的網格碰撞體都拋棄了,都換成了box collider。

三、真實的物理(剛體)很消耗,不要輕易使用,儘可能使用本身的代碼(數學計算)模仿假的物理。

四、最小化碰撞檢測請求(例如ray casts和sphere checks),儘可能從每次檢查中得到更多信息。
項目中涉及到物體的組件不多,關於physic的優化確定還有不少能夠說的,須要再去學習了。

VSync

簡單地說,這是CPU優化的最直接的一個方法。

科普:VSync垂直同步又稱場同步(Vertical Hold),垂直同步信號決定了CRT從屏幕頂部畫到底部,再返回原始位置的時間。從CRT顯示器的顯示原理來看,單個像素組成了水平掃描線,水平掃描線在垂直方向的堆積造成了完整的畫面。顯示器的刷新率受顯卡DAC控制,顯卡DAC完成一幀的掃描後就會產生一個垂直同步信號(決定於屏幕的刷新率)。咱們平時所說的打開垂直同步指的是將該信號送入顯卡3D圖形處理部分,從而讓顯卡在生成3D圖形時受垂直同步信號的制約(注意是制約)。

若是咱們選擇等待垂直同步信號(也就是咱們平時所說的垂直同步打開),那麼在遊戲中或許強勁的顯卡迅速的繪製完一屏的圖像,可是沒有垂直同步信號的到達,顯卡沒法繪製下一屏,只有等垂直同步的信號到達,才能夠繪製。這樣FPS天然要受到操做系統刷新率運行值的制約。而若是咱們選擇不等待垂直同步信號(也就是咱們平時所說的關閉垂直同步),那麼遊戲中做完一屏畫面,顯卡和顯示器無需等待垂直同步信號就能夠開始下一屏圖像的繪製,天然能夠徹底發揮顯卡的實力。可是不要忘記,正是由於垂直同步的存在,才能使得遊戲進程和顯示器刷新率同步,使得畫面更加平滑和穩定。

取消了垂直同步信號,當然能夠換來更快的幀率,可是在圖像的連續性上勢必打折扣。

4、Unity3D應用性能優化之GPU

通常人說DC的優化佔了unity3D軟件優化的三分天下,那麼GPU的優化也佔了三分天下。在瞭解GPU優化都有哪些着手點以前,咱們先了解一下GPU在3D軟件渲染中作了啥事:
圖片描述

頂點着色器

GPU接收頂點數據做爲輸入傳遞給頂點着色器。頂點着色器的處理單元是頂點,輸入進來的每一個頂點都會調用一次頂點着色器。(頂點着色器自己不能夠建立或銷燬任何頂點,並沒有法獲得頂點與頂點之間的關係)。頂點着色器是徹底可編程的,它主要完成的工做有:座標變換和逐頂點光照。 座標變換:就是對頂點的座標進行某種變換—把頂點座標從模型空間轉換到齊次裁剪空間。頂點的多少直接決定了三角形面的多少,也直接決定了GPU的渲染流水線的工做量,因此減小頂點數是一個比較重要的優化點。那麼減小頂點怎麼操做呢,又有哪些途徑?
一、優化基本幾何體
3D軟件都是從模型製做開始,在設計師建模的時候就要想到應該儘量地減小頂點數,一些對於模型沒有影響、或是肉眼很是難察覺到區別的頂點都要儘量去掉。好比在項目中,對於用戶背後的環境模型,一些樹木和石頭,視頻背面永遠沒法看見的神廟,能削減的都已經削減了。

二、使用LOD(Level of detail)技術
LOD技術有點相似於Mipmap技術,不一樣的是,LOD是對模型創建了一個模型金字塔,根據攝像機距離對象的遠近,選擇使用不一樣精度的模型。它的好處是能夠在適當的時候大量減小須要繪製的頂點數目。它的缺點一樣是須要佔用更多的內存,並且若是沒有調整好距離的話,可能會形成模擬的突變。

三、使用遮擋剔除(Occlusion culling)技術
遮擋剔除是用來消除躲在其餘物件後面看不到的物件,這表明資源不會浪費在計算那些看不到的頂點上,進而提高性能。剛纔神廟後面的剔除就屬於手動的遮擋剔除。

遮擋剔除是一個PRO版纔有的功能, 當一個物體被其餘物體遮擋住而不在攝像機的可視範圍內時不對其進行渲染。遮擋剔除在3D圖形計算中並非自動進行的。由於在絕大多數狀況下離 camera 最遠的物體首先被渲染,靠近攝像機的物體後渲染並覆蓋先前渲染的物體(這被稱爲重複渲染"overdraw"). 遮擋剔除不一樣於視錐體剔除. 視錐體剔除只是不渲染攝像機視角範圍外的物體而對於被其餘物體遮擋但依然在視角範圍內的物體,則不會被剔除. 注意當你使用遮擋剔除時,視錐體剔除(Frustum Culling)依然有效。

中間操做

一、曲面細分着色器:是一個可選的着色器,主要用於細分圖元。

二、幾何着色器:是一個可選的着色器,可用於執行逐圖元的着色操做,或者被用於產生更多的圖元。

三、裁剪:這一階段是可配置的。目的是把那些不在視野內的頂點裁剪掉,並剔除某些三角形圖元的面片。部分在視野內的圖元須要作裁剪處理,在裁剪邊緣產生新的頂點和三角形進行處理。

四、屏幕映射:這一階段是可配置和編程的,負責把每一個圖元的座標(三維座標系)轉換成屏幕座標(二維座標系)。

五、三角形設置:開始進入光柵化階段,再也不是數學上點了,而會把全部的點都映射到屏幕的具體像素座標上,計算每條邊上的像素座標而獲得三角形邊界的表示方式即爲三角形設置。

六、三角形遍歷:這一階段會檢查每一個像素是否被一個三角風格所覆蓋。若是覆蓋的話,就會生成一個片元(一個片元並非真正意義上的像素,而是包含了不少狀態的集合,這些狀態用於計算每一個像素的最終顏色。

這些狀態包括了屏幕座標、深度信息,及從幾何階段輸出的頂點信息,如法線和紋理座標等。),這樣一個查找哪些像素被三角形覆蓋的過程就是三角形遍歷。

片元着色器

片元着色器的輸入就是上一階段對頂點信息插值獲得的結果,更具體點說,是根據從頂點着色器中輸出的數據插值獲得的。而這一階段的輸出是一個或者多個顏色值。這一階段能夠完成不少重要的渲染技術,如紋理採樣,可是它的侷限在於,它僅能夠影響單個片元。片元着色器是比較花時間的,由於它是最終顏色的計算者,在某些狀況下,例如複雜燈光環境下,片元着色器會出現GPU流水線主要的拖後腿的存在。爲了讓片元着色器的計算更加快,咱們須要從不少方面進行提早的優化:
一、儘可能減小overdraw
片元着色器最容易拖後腿的狀況就是,overdraw!和Android app的開發同樣,就是同一個像素點繪製了屢次,某些狀況會形成計算力的浪費,增長耗電量。前面提到的遮擋剔除有減小overdraw很是有用。在PC上,資源無限,爲了獲得最準確的渲染結果,繪製順序多是從後往前繪製不透明物體,而後再繪製透明物體進行混合。可是在移動平臺上,對於不透明物體,咱們能夠設置從前日後繪製,對於有透明通道的物體(不少UI紋理就是含有透明通道的),再設置從後往前繪製。unity中shader設置爲「Geometry」 隊列的對象老是從前日後繪製的,而其餘固定隊(如「Transparent」「Overla」等)的物體,則都是從後往前繪製的。這意味這,咱們能夠儘可能把物體的隊列設置爲「Geometry」 。對於GUI,尤爲要注意和設計師商量,能用不透明的設計就用不透明的,對於粒子效果,也要注意不要引入透明值,多半狀況下,移動平臺的粒子效果透明值沒有做用。

二、減小實時光照
移動平臺的最大敵人。一個場景裏若是包含了三個逐像素的點光源,並且使用了逐像素的shader,那麼頗有可能將Draw Calls提升了三倍,同時也會增長overdraws。這是由於,對於逐像素的光源來講,被這些光源照亮的物體要被再渲染一次。更糟糕的是,不管是動態批處理仍是動態批處理(其實文檔中只提到了對動態批處理的影響,但不知道爲何實驗結果對靜態批處理也沒有用),對於這種逐像素的pass都沒法進行批處理,也就是說,它們會中斷批處理。

因此當你須要光照效果時,可使用Lightmaps,提早烘焙好,提早把場景中的光照信息存儲在一張光照紋理中,而後在運行時刻只須要根據紋理採樣獲得光照信息便可。當你須要金屬性強(鏡面)的效果,可使用Light Probes。當你須要一束光的時候,可使用體積光去模擬這個效果。

三、不要使用動態陰影
動態陰影很酷,可是對於片元着色器來講是災難,陰影計算是三角投影計算,很是耗性能。若是想要陰影,可使用 a、簡單的使用一個帶陰影的貼圖 b、烘焙場景,拿到lightmaps c、建立投影生成器的方法 d、使用ShadowMap的方法(目前尚未研究)。

四、儘可能使用簡單的shader
a、建議儘可能實用Unity自帶mobile版本的(built-in)Shader,這些大大提升了頂點處理的性能。固然也會有一些限制。
b、本身寫的shader請注意複雜操做符計算,相似pow,exp,log,cos,sin,tan等都是很耗時的計算,最多隻用一次在每一個像素點的計算,還有有些除法運算儘可能該能乘法運算等。
c、避免透明度測試着色器,由於這個很是耗時,使用透明度混合的版原本代替。
d、浮點類型運算:精度越低的浮點計算越快。
e、不要在Shader中添加沒必要要的Pass.

5、Unity3D應用性能優化以內存

unity中有兩類內存,一個是Mono託管的內存(至關於DVM的內存),一個是Unity3D使用的資源類類型的內存(Texture、Mesh這種)。

Mono內存

一、儘可能不要動態的Instantiate和Destroy Object,使用Object Pool。

二、不要動態的產生字符串,使用字符串的直接拼接,使用System.Text.StringBuilder代替。

三、Cache一些東西,在update裏面儘可能避免search,如GameObject.FindWithTag("")、GetComponent這樣的調用,能夠在Start中預先存起來。

四、儘可能減小函數調用棧,用x = (x > 0 ? x : -x);代替x = Mathf.Abs(x)。

五、定時重複處理用 InvokeRepeating 函數實現。

六、減小GetComponent的調用,使用 GetComponent或內置組件訪問器會產生明顯的開銷。您能夠經過一次獲取組件的引用來避免開銷,並將該引用分配給一個變量(transform用的最多)。

七、使用內置數組,內置數組是很是快的。ArrayList或Array類很容易使用,你能輕易添加元件。可是他們有徹底不一樣的速度。 內置數組有固定長度,而且大多時候你會事先知道最大長度而後填充它。內置數組最好的一點是他們直接嵌入結構數據類型在一個緊密的緩存裏,而不須要任何額外 類型信息或其餘開銷。所以,在緩存中遍歷它是很是容易的,由於每一個元素都是對齊的。

Unity3D類的內存

這類內存包括
一、AssetBundle
Unity3D 裏有兩種動態加載機制:一個是Resources.Load,另一個經過AssetBundle,其實二者區別不大。 Resources.Load就是從一個缺省打進程序包裏的AssetBundle里加載資源,而通常AssetBundle文件須要你本身建立,運行時 動態加載,能夠指定路徑和來源的。

AssetBundle運行時加載:

(1)來自文件就用CreateFromFile(注意這種方法只能用於standalone程序,就不提了)。

(2)也能夠來自Memory,用CreateFromMemory(byte[]),這個byte[]能夠來自文件讀取的緩衝,www的下載或者其餘可能的方式。其實WWW的assetBundle就是內部數據讀取完後自動建立了一個assetBundle而已,Create完之後,等於把硬盤或者網絡的一個文件讀到內存一個區域,這時候只是個AssetBundle內存鏡像數據塊,尚未Assets的概念。

下圖是AssetBundle的加載卸載示意圖:
圖片描述

AssetBundle是如何加載的呢?用AssetBundle.Load(同Resources.Load) 這纔會從AssetBundle的內存鏡像裏讀取並建立一個Asset對象,建立Asset對象同時也會分配相應內存用於存放(反序列化)。異步讀取用AssetBundle.LoadAsync,也能夠一次讀取多個用AssetBundle.LoadAll。

AssetBundle如何釋放呢?

AssetBundle.Unload(flase)是釋放AssetBundle文件的內存鏡像,不包含Load建立的Asset內存對象。

AssetBundle.Unload(true)是釋放那個AssetBundle文件內存鏡像和並銷燬全部用Load建立的Asset內存對象。

二、Texture
對於IOS選擇使用 PVRTC壓縮格式的,對於Android選擇ETC壓縮格式的,紋理能夠節省大量內存和讀取速度快,可是會有所下降圖像的質量。

2D紋理若是沒有必要不要使用mimap(會約增長33%的內存開銷),曾經在IOS上吃過虧。3D模型的紋理通常是須要mimap的,可是若是肯定了3D模型距離攝像機的距離,在GPU分析器上肯定了unity使用的紋理,就能夠保留,關閉mimap(好比項目中的avatar)。

3.Mesh
有Mesh合併和Mesh壓縮(坑比較多,不建議使用)。

4.Particle
粒子效果只要記住使用以後及時釋放銷燬就行。

針對手遊的性能優化,騰訊WeTest平臺的Cube工具提供了基本全部相關指標的檢測,爲手遊進行最高效和準確的測試服務,不斷改善玩家的體驗。

目前功能還在免費開放中。,歡迎點擊連接:
http://wetest.qq.com/product/cube 使用。

相關文章
相關標籤/搜索