Unity對(duì)Mesh/Texture資源的上傳GPU機(jī)制盯蝴,allowSceneActivation和ResetPreMappedBufferMemory接口作用分享

1 資源是如何上傳到GPU的

比起干說(shuō),還是結(jié)合Unity Profiler做實(shí)驗(yàn)來(lái)得直觀和有說(shuō)服力抱既。

首先準(zhǔn)備一個(gè)帶Mesh的Prefab职烧,保證從磁盤(pán)加載到CPU內(nèi)存之前游戲場(chǎng)景中沒(méi)有其他實(shí)例在使用該P(yáng)refab所指向的Mesh資源,然后在運(yùn)行過(guò)程的某個(gè)時(shí)刻開(kāi)始防泵,先后調(diào)用Load和Unload邏輯蚀之,通過(guò)Unity的Profiler觀察GPU內(nèi)存總量變化和上傳數(shù)據(jù)變化。

public class TestUpload : MonoBehaviour
{
    private List<GameObject> tps = new List<GameObject>();

    void Start()
    {
        StartCoroutine(CountDown());
    }

    IEnumerator CountDown()
    {
        yield return new WaitForSeconds(0.2f);
        JustLoad("Assets/test.prefab");
        yield return new WaitForSeconds(0.2f);
        ReleaseAsset(0);
    }

    private void JustLoad(string path)
    {
        var go = AssetDatabase.LoadAssetAtPath<GameObject>(path); //只加載捷泞,甚至不激活和顯示
        tps.Add(go);
    }

    private void ReleaseAsset(int idx)
    {
        tps[idx] = null;
        EditorUtility.UnloadUnusedAssetsImmediate(); //使用此接口及時(shí)觸發(fā)GPU端資源釋放
    }

    private void OnDestroy()
    {
        tps.Clear();
    }
}

下圖來(lái)自Profiler:

在Unity運(yùn)行期間足删,加載資源(Mesh或Texture)完成的同時(shí),無(wú)論當(dāng)前幀是否有渲染目標(biāo)Mesh或者使用目標(biāo)Texture的需要(即便只是加載了資源锁右,之后什么都不操作)都會(huì)觸發(fā)向GPU上傳數(shù)據(jù)的操作失受,并被保存在名叫GfxBuffer的內(nèi)部類(lèi)中。傳輸模式在Editor模式下是同步的咏瑟,會(huì)在觸發(fā)上傳的同一幀內(nèi)完成向GPU提交全部數(shù)據(jù)(Header和Binary)拂到。 同樣,當(dāng)Unity不再持有該Mesh資源的引用码泞,并且在尋求主動(dòng)釋放GPU資源時(shí)兄旬,顯存中的關(guān)聯(lián)資源(Vertex/Index Buffer等)才會(huì)被釋放,在Editor模式下浦夷,這個(gè)操作對(duì)應(yīng)了EditorUtility.UnloadUnusedAssetsImmediate()方法辖试。

為了知道Unity在上傳GPU資源數(shù)據(jù)時(shí)到底干了什么,這邊比較了一下上傳關(guān)鍵幀和其他時(shí)間CPU部分負(fù)載的異同劈狐,利用Profiler自帶Hierarchy查找關(guān)鍵詞Mesh罐孝,比較異同后發(fā)現(xiàn):處于上傳負(fù)載的關(guān)鍵幀時(shí)期,Unity觸發(fā)了2個(gè)獨(dú)特的函數(shù)調(diào)用:

  1. Mesh.AwakeFromLoad
  2. Mesh.CreateMesh

這兩個(gè)方法在Prolier中的具體執(zhí)行層級(jí)如下圖所示:

可見(jiàn)當(dāng)讀取磁盤(pán)數(shù)據(jù)的回調(diào)一旦完成肥缔,就觸發(fā)了Mesh.AwakeFromLoad莲兢,進(jìn)而觸發(fā)了Mesh.CreateMesh,而這個(gè)方法內(nèi)部主要負(fù)責(zé)將Mesh中的"Vertice"和"Index"數(shù)據(jù)依序通過(guò)GeometryBuffer上傳到GPU端续膳。

進(jìn)一步梳理下Mesh和Texture等資源的處理流程改艇,可以區(qū)分為兩種方式:

  1. 與場(chǎng)景同時(shí)加載
    • 其本質(zhì)也是從磁盤(pán)加載,但是隨同場(chǎng)景出現(xiàn)而出現(xiàn)
    • 在調(diào)用Profiler測(cè)試時(shí)坟岔,往往因?yàn)閳?chǎng)景早于Profiler工作而出現(xiàn)谒兄,所以一部分基于場(chǎng)景的Mesh和Texture早已在GPU了
  2. 運(yùn)行時(shí)由代碼觸發(fā)的從磁盤(pán)加載
    • 這里如果是從構(gòu)建好的AssetBundle中獲取數(shù)據(jù),那么就有2中不同的上傳模式:
      1. Sync:
        • 該模式在資源build時(shí)期(就是打AssetBundle時(shí)期)會(huì)將數(shù)據(jù)的Header和Binary全部打包到.res文件內(nèi),
        • 在游戲運(yùn)行時(shí),Unity從磁盤(pán)(Bundle)中讀取這個(gè)文件到內(nèi)存晾腔,之后會(huì)由主線程于一幀內(nèi)將資源(Header和Binary)Upload到GPU锐峭。
      2. Async
        • 還是在資源build時(shí)期,會(huì)將Header寫(xiě)入.res文件中妇多,Binary數(shù)據(jù)則寫(xiě)入.resS文件中
        • 在游戲運(yùn)行時(shí),Unity從磁盤(pán)(Bundle)中讀取.res文件到內(nèi)存,解析出Header數(shù)據(jù)啊研,之后Unity采用streams的方式從.resS文件中加載Binary數(shù)據(jù)到GPU,這個(gè)過(guò)程使用了一個(gè)固定大小的Ring Buffer(環(huán)形緩沖)鸥拧,而且還會(huì)利用多線程党远,分多幀處理。
    • 如果是從Resources目錄讀取資源住涉,或者在Editor模式下調(diào)用AssetDatabase獲取資源麸锉,那么統(tǒng)一走Sync模式

備注1 Unity重復(fù)使用一段環(huán)形緩沖作為流式(Streaming)上傳數(shù)據(jù)到GPU的區(qū)域,這么做的主要目的是避免重復(fù)開(kāi)辟新的內(nèi)存舆声。在ProjectSettings->Quality->AsyncAssetUpload->BufferSize可以控制環(huán)形緩沖的大小花沉,默認(rèn)是4MB,最小可調(diào)到2MB媳握,最大則是2GB碱屁。當(dāng)單個(gè)Mesh或Texture的尺寸超過(guò)環(huán)形緩沖大小時(shí),Unity不得不重新開(kāi)辟RingBuffer以適應(yīng)上傳數(shù)據(jù)大小蛾找,出現(xiàn)這種情況會(huì)導(dǎo)致效率下降娩脾,因此最佳策略是手動(dòng)調(diào)整BufferSize,以滿足場(chǎng)景內(nèi)最大Mesh/Texture的尺寸打毛。

備注2 Unity也提供了控制每幀Upload時(shí)間(ms)的接口和設(shè)置柿赊,可以在ProjectSettings->Quality->AsyncAssetUpload->TimeSlice中調(diào)節(jié)每一幀最大可占用的CPU時(shí)長(zhǎng)俩功,這個(gè)數(shù)值越大,意味著GPU將越快獲得Mesh/Texture數(shù)據(jù)碰声,代價(jià)是CPU在提交數(shù)據(jù)的這段時(shí)間內(nèi)負(fù)荷增大诡蜓。

注意只有在出發(fā)C#加載Mesh資源的那一幀(上傳關(guān)鍵幀),系統(tǒng)才向GPU上傳了一定數(shù)量是頂點(diǎn)和索引緩沖數(shù)據(jù)胰挑。那一幀過(guò)后蔓罚,系統(tǒng)恢復(fù)“常態(tài)”。

部分關(guān)鍵參數(shù)名的含義參考如下官方文檔:

Vertex Buffer Upload In Frame Count/Bytes The amount of geometry that the CPU uploaded to the GPU in the frame. This represents the vertex/normal/texcoord data. There might already be some geometry on the GPU. This statistic only includes geometry that Unity transfers in a frame.
Index Buffer Upload In Frame Count/Bytes The amount of geometry that the CPU uploaded to the GPU in the frame. This represents the triangle indices data. There might already be some geometry on the GPU. This statistic only includes geometry that Unity transfers in a frame.

2 allowSceneActivation

這個(gè)值的作用參考文檔即可:AsyncOperation.allowSceneActivation

簡(jiǎn)單來(lái)說(shuō)瞻颂,我們可以通過(guò)在加載場(chǎng)景前將該變量設(shè)置為false豺谈,從而控制Unity專(zhuān)心于該場(chǎng)景的異步加載,直到完成度達(dá)到90%后(也可以提前贡这,但是不能延后)再通過(guò)將allowSceneActivation設(shè)置為true從而重啟其他處于隊(duì)列中等待的AsyncOperation茬末,比如Unity官方提到的SceneManager.UnloadSceneAsync

我想說(shuō)的是藕坯,有時(shí)候如果沒(méi)有及時(shí)提前觸發(fā)非場(chǎng)景類(lèi)的AssetBundle異步加載团南,那么allowSceneActivation也會(huì)將這些Bundle的加載停住(stalled)炼彪,由于是異步提交的吐根,因此誤停其他Bundle加載也很可能是偶發(fā)的,不一定在測(cè)試的時(shí)候必現(xiàn)辐马,需要注意和提前規(guī)避拷橘。

3 ResetPreMappedBufferMemory

這個(gè)借口的作用參考這篇官方文檔:ParticleSystem.ResetPreMappedBufferMemory

之所以提及這個(gè)接口是因?yàn)橛许?xiàng)目遇到一個(gè)戰(zhàn)斗中內(nèi)存突然暴增的問(wèn)題,特別是在以高倍速播放戰(zhàn)斗畫(huà)面的情況下喜爷,Gfx Memory會(huì)有倍增的恐怖效果冗疮,而且戰(zhàn)斗結(jié)束一段時(shí)間后爆漲的內(nèi)存仍然沒(méi)有明顯回落。 導(dǎo)致這個(gè)問(wèn)題的原因是同屏粒子特效過(guò)多檩帐,使得Unity粒子系統(tǒng)底層申請(qǐng)了較大緩存用來(lái)存放Mesh等粒子渲染資源术幔。

很顯然Unity底層有專(zhuān)門(mén)算法控制額外內(nèi)存申請(qǐng)量,我們的游戲戰(zhàn)斗在高倍速播放過(guò)程中累積了大量同屏粒子特效的顯示請(qǐng)求湃密,這個(gè)請(qǐng)求量顯然嚇到了Unity诅挑。粗暴的解決方法是在內(nèi)存申請(qǐng)高峰之后,適時(shí)的調(diào)用ParticleSystem.ResetPreMappedBufferMemory()方法重置這部分額外開(kāi)辟的內(nèi)存泛源。

一個(gè)疑問(wèn)是Unity的這項(xiàng)預(yù)申請(qǐng)大量?jī)?nèi)存的優(yōu)化是否僅針對(duì)大內(nèi)存設(shè)備啟用拔妥,或者會(huì)依據(jù)可用內(nèi)存大小自動(dòng)調(diào)整?因?yàn)槿绻彺嬷凳歉鶕?jù)可用內(nèi)存來(lái)的確定的上限的达箍,那么短時(shí)間內(nèi)存占用的爆發(fā)也算是一種可控范圍內(nèi)的技術(shù)處理没龙,我們無(wú)需額外修正,不過(guò)后來(lái)的測(cè)試表明并不是(至少2021版還不是)

分析上圖,沒(méi)有在4G手機(jī)上找到明顯的安可用比例申請(qǐng)內(nèi)存大小的證據(jù)硬纤。

當(dāng)然解滓,優(yōu)雅的解決方案是控制任何可能短時(shí)間內(nèi)大量生成粒子特效的情景,這其中包括了高倍速播放戰(zhàn)斗筝家,也包括其他諸如同屏多角色釋放大量粒子特效伐蒂,甚至粒子特效的制作本身。

Ref

Manual/ProfilerRendering

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肛鹏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恩沛,更是在濱河造成了極大的恐慌在扰,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雷客,死亡現(xiàn)場(chǎng)離奇詭異芒珠,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)搅裙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)皱卓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人部逮,你說(shuō)我怎么就攤上這事娜汁。” “怎么了兄朋?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵掐禁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我颅和,道長(zhǎng)傅事,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任峡扩,我火速辦了婚禮蹭越,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘教届。我一直安慰自己响鹃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布巍佑。 她就那樣靜靜地躺著茴迁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪萤衰。 梳的紋絲不亂的頭發(fā)上堕义,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼倦卖。 笑死洒擦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的怕膛。 我是一名探鬼主播熟嫩,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼褐捻!你這毒婦竟也來(lái)了掸茅?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤柠逞,失蹤者是張志新(化名)和其女友劉穎昧狮,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體板壮,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逗鸣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绰精。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撒璧。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖笨使,靈堂內(nèi)的尸體忽然破棺而出卿樱,到底是詐尸還是另有隱情,我是刑警寧澤硫椰,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布殿如,位于F島的核電站,受9級(jí)特大地震影響最爬,放射性物質(zhì)發(fā)生泄漏涉馁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一爱致、第九天 我趴在偏房一處隱蔽的房頂上張望烤送。 院中可真熱鬧,春花似錦糠悯、人聲如沸帮坚。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)试和。三九已至,卻和暖如春纫普,著一層夾襖步出監(jiān)牢的瞬間阅悍,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留节视,地道東北人拳锚。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像寻行,于是被迫代替她去往敵國(guó)和親霍掺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容