寫在前面
Unity自從2018.3版本開始推出了紋理串流系統(tǒng),即Texture Streaming System,在Unity官方文檔中,其名為The Mipmap Streaming System。雖然說紋理串流系統(tǒng)是一項比較先進的技術(shù),若使用得當(dāng)可以有效減少紋理占用內(nèi)存與提高加載速度,但在使用的過程中依然會面臨不少的坑,導(dǎo)致目前實際使用該系統(tǒng)的項目并不算多。本文將淺談Unity的紋理串流系統(tǒng),對該系統(tǒng)的運行策略進行合理猜測,并對其眾多的參數(shù)與Texture原生的一些參數(shù)進行測試,對其進行簡單分析和總結(jié),如果有不對的地方歡迎批評斧正。
Unity版本:2021.3.12f1
Platform: Antroid
渲染管線:URP
測試機:HUAWEI P30 測試工程
Github地址:https://github.com/recaeee/Unity-Mipmap-And-Texture-Streaming
Mipmap
Mipmap本身原理在此就不做過多介紹(Mip實際是原始紋理的下采樣版本),網(wǎng)上有很多相關(guān)文章,推薦觀看GAMES 101課程,,其主要目的就是解決摩爾紋等現(xiàn)象(Pixel和Texel實際尺寸不匹配)。當(dāng)一個Texture開啟Mipmap后,其占用的內(nèi)存會變?yōu)樵镜?/3倍。值得注意的是,Mipmap本身是針對于3D物體而言的,對于如UI使用的Texture,我們幾乎是不需要對其開啟Mipmap的。
首先在這里詳細說一下Unity在運行時是如何使用Mipmap的(不開啟Texture Streaming,只考慮移動平臺,各平臺原理類似,內(nèi)存結(jié)構(gòu)可能有所不同)。
參考Unity官方文檔:
https://docs.unity3d.com/cn/2021.3/Manual/texture-mipmaps-introduction.html
1. 當(dāng)渲染一個使用Mipmap紋理的GO時,CPU會先從磁盤上把該Texture的所有Mip等級都加載到顯存中。
2. 當(dāng)GPU對紋理進行采樣時,它會根據(jù)當(dāng)前像素的紋理坐標(biāo)和GPU計算的兩個內(nèi)部值DDX和DDY來確定要使用的Mipmap等級,也就是根據(jù)像素在場景中覆蓋的實際大小找到與之匹配的Texel大小,根據(jù)該Texel大小決定要使用的Mip等級。補充說明,DDX和DDY提供有關(guān)當(dāng)前像素旁邊和上方像素的UV的信息,包括距離和角度,GPU使用這些值來確定有多少紋理細節(jié)對相機可見。
紋理串流 Texture Streaming
在Unity中,紋理串流技術(shù)叫做The Mipmap Streaming System,其作用是讓Unity根據(jù)攝像機的位置只加載對應(yīng)Mipmap Level的紋理到顯存中,而不是把所有Mipmap Level全加載到顯存中讓GPU根據(jù)攝像機位置使用對應(yīng)的Mipmap Level。
1. Mipmap加載疑問
在這里,我有一點疑問,在開啟Texture Streaming后,如果說加載的Mipmap Level為2,那更高級的Mipmap Level會加載進顯存嗎?因此我搭建了測試工程進行試驗。
在測試工程中我使用了一張2048*2048且開啟Mipmap的紋理,首先不開啟Texture Streaming,打包到真機測試,抓取內(nèi)存,發(fā)現(xiàn)這張Texture共占用了2.7M的內(nèi)存。而在不開啟Mipmap時,該紋理占用內(nèi)存為2.0MB,可得開啟Mipmap后該紋理占用內(nèi)存變?yōu)樵瓉淼?.35倍(與4/3倍接近)。因為0級Mip占用內(nèi)存2M,我們可以大致推算出1級Mip占用內(nèi)存為0.5MB,2級Mip占用內(nèi)存為128KB,3級Mip占用內(nèi)存為32KB。

接下來開啟Texture Streaming并通過Texture Streaming System使Mip加載到Mip1等級,抓取內(nèi)存,如下圖所示Texture占用了0.7MB,由此得出,僅僅Mip0被卸載,而Mip1~n都依然在顯存中。

同理,再讓其加載到Mip2,抓取內(nèi)存,如下圖所示Texture占用了171KB,推理可得出為(2.7-2-0.5)MB,接近0.2MB。

由此可得出結(jié)論:一張Texture具有0~n級Mip,開啟Texture Streaming之后,如果Unity計算出的Mip等級為x,則Unity會將該Texture的x~n級Mip都加載到顯存中,也可以理解為丟棄掉0~(x-1)級Mip。(在后文中所說加載Mip等級x,意思都是加載Mip等級x~n。)
其實也非常有道理,對于使用開啟Mipmap的紋理的一個GO,我們不一定只會用到1張Mip,因為如果GO是一個長條型,遠處的像素可能還是會使用更高等級的Mip。只是說Texture Streaming System會把必不可能用到的幾個低等級的Mip丟棄,不加載到顯存中(或者從顯存中卸載)。
2. Texture Streaming加載邏輯
在開啟Texture Streaming后,Unity使用Mipmap的方式會發(fā)生一定變化,因為顯存中只會存儲需要加載的Mipmap Level。其運行時使用步驟大致如下(開啟Texture Streaming,不考慮紋理串流預(yù)算、MaxLevelReduction等因素,后面會詳細說明)。
(1)當(dāng)渲染一個使用Mipmap紋理的GO時,CPU將最低Mipmap等級(人為設(shè)置)的Mip異步加載到顯存中。
(2)GPU先使用這些低級Mipmap渲染GO。
(3)CPU計算出該GO必不可能用到Mip等級,比如計算出x意味著只可能會用到x+1~n級Mip,將x+1~n級Mip加載到顯存中。
(4)當(dāng)GPU對紋理進行采樣時,它會根據(jù)當(dāng)前像素的紋理坐標(biāo)和GPU計算的兩個內(nèi)部值DDX和DDY來確定要使用的Mipmap等級,也就是根據(jù)像素在場景中覆蓋的實際大小找到與之匹配的Texel大小,根據(jù)該Texel大小決定要使用的Mip等級。補充說明,DDX和DDY提供有關(guān)當(dāng)前像素旁邊和上方像素的UV的信息,包括距離和角度,GPU使用這些值來確定有多少紋理細節(jié)對相機可見。
2.1 紋理異步加載 AUP
使用Texture Streaming有一個好處是,加載一個物體時會先異步加載一個較高等級的Mip,讓物體被較快地渲染出來,之后再使用較低等級的Mip,展現(xiàn)高精度的紋理細節(jié)。反應(yīng)到游戲內(nèi)就是,加載物體時,先呈現(xiàn)出較為模糊的紋理,再呈現(xiàn)出較為精細的紋理。
這里提幾句紋理異步加載的原理(異步上傳管線AUP),主要參考了官方文檔和文章《優(yōu)化加載性能:了解異步上傳管線AUP》。
在同步上傳管線中,Unity必須在單個幀中同時加載紋理或網(wǎng)格的元數(shù)據(jù)(標(biāo)頭數(shù)據(jù))、紋理的每個Texel或網(wǎng)格的每個頂點數(shù)據(jù)(二進制數(shù)據(jù))。在異步上傳管線中,Unity必須在單個幀中僅加載標(biāo)頭數(shù)據(jù),并在后續(xù)幀中將二進制數(shù)據(jù)流式傳輸?shù)紾PU。
同步上傳管線中,在項目構(gòu)建時,Unity會將同步加載的網(wǎng)格或紋理的標(biāo)頭數(shù)據(jù)和二進制數(shù)據(jù)都寫入同一.res文件(res即Resource)。在運行時,當(dāng)程序同步加載紋理或網(wǎng)格時,Unity將該紋理或網(wǎng)格的標(biāo)頭數(shù)據(jù)和二進制數(shù)據(jù)從.res文件(磁盤中)加載到內(nèi)存(RAM)中。當(dāng)所有數(shù)據(jù)都位于內(nèi)存中時,Unity隨后將二進制數(shù)據(jù)上傳到GPU(DrawCall前)。加載和上傳操作都發(fā)生在主線程上的單個幀中。
異步上傳管線中,在項目構(gòu)建時,Unity會將標(biāo)頭數(shù)據(jù)寫入到一個.res文件,而將二進制數(shù)據(jù)寫入到另一個.resS文件(S應(yīng)該指Streaming)。在運行時,當(dāng)程序異步加載紋理或網(wǎng)格時,Unity將標(biāo)頭數(shù)據(jù)從.res文件(磁盤中)加載到內(nèi)存(RAM)中。當(dāng)標(biāo)頭數(shù)據(jù)位于內(nèi)存中時,Unity隨后使用固定大小的環(huán)形緩沖區(qū)(一塊可配置大小的緩沖區(qū))將二進制數(shù)據(jù)從.resS文件(磁盤中)流式傳輸?shù)紾PU。Unity使用多個線程通過幾幀流式傳輸二進制數(shù)據(jù)。
此外需要注意的是,如果項目構(gòu)建在安卓平臺,需要啟用LZ4壓縮才能啟用紋理異步加載(而因為Texture Streaming System使用到了紋理異步加載,因此Texture Streaming System的前提條件也是LZ4壓縮)。
從Unity 2018.3 beta開始,資源上傳管線Async Upload Pipeline用于異步加載紋理和網(wǎng)格(可讀寫紋理和網(wǎng)格、壓縮網(wǎng)格不適用于AUP)。
在AUP異步加載紋理時,第一幀將會加載.res標(biāo)頭數(shù)據(jù)(紋理的元數(shù)據(jù))到內(nèi)存中,再流式傳輸.resS二進制數(shù)據(jù)(紋理的每個Texel)。在流式傳輸.resS過程中,AUP會執(zhí)行以下邏輯。
(1)等待CPU上的環(huán)形緩沖區(qū)內(nèi)出現(xiàn)空余的內(nèi)存空間。
(2)CPU從磁盤上的.resS文件中讀取一部分二進制數(shù)據(jù),將其填入第1步中環(huán)形緩沖區(qū)的空余內(nèi)存內(nèi)。
(3)執(zhí)行一些后期處理過程,例如紋理解壓、網(wǎng)格碰撞生成、每個平臺的修復(fù)等。
(4)以時間切片的方式在渲染線程進行上傳,即每一幀花n個時間切片的CPU時間將環(huán)形緩沖區(qū)內(nèi)的數(shù)據(jù)(數(shù)據(jù)量由時間切片的持續(xù)時間決定)傳遞給GPU。
(5)釋放環(huán)形緩沖區(qū)上已傳遞給GPU過的內(nèi)存。
AUP在運行時可控制的參數(shù)包含三個,分別為QualitySettings.asyncUploadTimeSlice(即每幀內(nèi)第4步時間切片的時間總量)、QualitySettings.asyncUploadBufferSize(環(huán)形緩沖區(qū)的大?。ualitySettings.asyncUploadPersistentBuffer(決定完成當(dāng)前所有讀取工作后,是否釋放環(huán)形緩沖區(qū))。這三個參數(shù)具體如何使用可參考文章《優(yōu)化加載性能:了解異步上傳管線AUP》,在此不做過多深入。
3. 使用Texture Streaming
以下為使用Texture Streaming的一些說明。
3.1 設(shè)置參數(shù)
這一塊比較多參考官方文檔,更詳細的說明請參考官方文檔。

Add All Cameras:是否對項目中的所有攝像機開啟紋理串流。(默認(rèn)開啟)
Memory Budget:設(shè)置開啟紋理串流時加載的紋理內(nèi)存最大值。(默認(rèn)512MB)
Renderers Per Frame:設(shè)置CPU每幀紋理串流系統(tǒng)處理多少個Renderers(即對于一個Renderer,在CPU端計算出需要傳遞哪幾級Mipmap并傳遞給顯存)。該值較低時會降低每幀CPU處理紋理串流的壓力,但會增加Unity加載Mipmap的延遲。
Max Level Reduction:設(shè)置當(dāng)超過紋理預(yù)算時紋理串流能丟棄的最大Mipmap數(shù)。(默認(rèn)是2,意味著最多丟棄2張最低級的Mipmap)同時,這個值也決定了紋理初始化加載時會加載Max Level Reduction級的Mipmap。
Max IO Requests:設(shè)置紋理串流系統(tǒng)的紋理文件IO請求的最大數(shù)量。(默認(rèn)是1024)
3.2 讓紋理支持Mipmap Streaming
選中需要啟用紋理串流的Texture Asset,在其Inspector的Advanced標(biāo)簽下勾選Streaming Mipmaps。對于安卓平臺開發(fā),需要在Build Settings中使用LZ4或者LZ4HC的壓縮格式。Unity需要這些壓縮方式來實現(xiàn)異步紋理加載,異步紋理加載是實現(xiàn)紋理串流系統(tǒng)的必要技術(shù)。
在對Texture Asset勾選Streaming Mipmaps之后,出現(xiàn)Mip Map Priority屬性,該屬性表示該紋理在Mipmap Streaming System中分配資源的優(yōu)先級。Priority值越大,優(yōu)先級越高,其范圍為[-128,127]。

Lightmaps同樣支持紋理串流,操作方式和Texture Asset一樣。但是在Unity重新生成光照貼圖時,其設(shè)置會重置為默認(rèn)值。通過在Project Settings里可以設(shè)置生成光照貼圖時對應(yīng)紋理串流系統(tǒng)的默認(rèn)配置。

3.3 配置Mipmap Streaming
首先,我們需要配置Memory Budget,即內(nèi)存預(yù)算,當(dāng)運行時Texture占用內(nèi)存超過Memory Budget時,Unity會自動丟棄沒有使用到的Mipmaps,通過設(shè)置Max Level Reduction屬性可以控制Unity丟棄的Mipmaps。同時Max Level Reduction也代表了Mipmap Streaming System在初始加載一張Texture時加載的Mipmap等級。
注意:Max Level Reduction在Mipmap Streaming System中優(yōu)先級比Memory Budget高,意味著即使會超出Budget,紋理依舊會加載Max Level Reduction級別的Mip到顯存中。
3.4 配置攝像機
當(dāng)開啟Mipmap Streaming System后,Unity默認(rèn)會對所有攝像機啟用它。我們可以通過在Quality Settings中通過設(shè)置Add All Cameras來配置是否對所有攝像機開啟Mipmap Streaming System。

如果說想對單獨的攝像機做配置,我們需要在攝像機上增加一個Streaming Controller組件,如果不想要讓這個攝像機開啟紋理串流,則直接Disable這個組件。同時這個組件也允許我們?nèi)フ{(diào)整該攝像機的Mip偏移。

如果說項目中UI使用單獨一個攝像機渲染,那我們就沒必要對UICamera也開啟紋理串流,因此沒必要在QualitySettings中激活A(yù)dd All Cameras,只需要在渲染場景的攝像機上增加Streaming Controller組件。
3.5 配置啟用環(huán)境
Mipmap Streaming默認(rèn)只在Play Mode下啟用,我們可以在Editor Settings中設(shè)置它在Editor、Play兩個Mode中是否啟用。

3.6 調(diào)試Mipmap串流
在Built-in管線中,Unity的Scene視圖中的下拉菜單中會有一個Texture Streaming的繪制模式,它會根據(jù)游戲?qū)ο笤贛ipmap系統(tǒng)中的狀態(tài),顯示為綠色、紅色和藍色。具體可以參考官方文檔。
對于非Built-in管線,可能就需要在管線中手動實現(xiàn)Shader替換(比如插入一個Render Feature)。
3.7 一些常用參數(shù)
Texture.currentTextureMemory:所有紋理當(dāng)前使用的內(nèi)存量。
Texture.streamingTextureDiscardUnusedMips:該值默認(rèn)為False,當(dāng)其設(shè)置為True時,Unity會強制紋理串流系統(tǒng)去丟棄所有未使用的Mipmap而不是緩存它們。因為紋理串流系統(tǒng)也是使用一個類似內(nèi)存池的管理方式,假設(shè)物體距離攝像機變遠,此時計算出的Mip等級變高,但紋理串流系統(tǒng)不會立即卸載低級Mip并使用計算出的Mip,而是當(dāng)其他紋理需要加載且Budget不夠時再考慮卸載當(dāng)前內(nèi)存池中未使用到的Mips,即從遠到近查找未使用的Mip(這一點經(jīng)過測試驗證過,測試結(jié)果如下兩圖所示)。


從這點看出,當(dāng)Texture.streamingTextureDiscardUnusedMips未開啟時,Mipmap Streaming System的卸載無用Mip邏輯是被動觸發(fā)的(類似于事件,當(dāng)新紋理加載且預(yù)算不足時觸發(fā),應(yīng)該是會降低CPU計算量,也是利用上緩存,減少IO消耗)。而當(dāng)Texture.streamingTextureDiscardUnusedMips開啟后,Mipmap Streaming System的卸載邏輯變?yōu)?b>每幀主動觸發(fā),即每幀都會嚴(yán)格按照Mip計算出的等級實際應(yīng)用于GO上。
4. Texture Streaming System管理策略
其實網(wǎng)上對于Texture Streaming System具體的解析少之又少,在此我首先參考UWA學(xué)堂《Unity引擎加載模塊和內(nèi)存管理的量化分析及優(yōu)化方法》中對Texture Streaming System策略進行簡單概括,有興趣的同學(xué)可以購買原課程觀看。
對于整個紋理串流系統(tǒng)來說,最重要的兩個參數(shù)就是Memory Budget(紋理串流預(yù)算)和Max Level Reduction()。Memory Budget決定了當(dāng)紋理內(nèi)存占用到多少之后Unity開始真正使用Texture Streaming System去管理Mipmap的內(nèi)存;Max Level Reduction決定了最大加載的Mipmap等級(Mipmap等級越高越模糊)。對于Memory Budget來說,Unity的默認(rèn)值為512MB,但是對于一般的手機項目而言,設(shè)置為200MB左右比較合適(但實際看需要修改該值)。
對于運行時,Texture Streaming System管理策略概括如下:
(1)當(dāng)Non Streamed Texture(未開啟Mipmap Streaming的Texture)需要被加載時,其會被完全加載到內(nèi)存中,如果加載的Texture具有Mipmap 0~n,則Mipmap 0~n都會被加載到內(nèi)存中。
(2)在加載Scene時,如果Budget足夠,Scene中的GO所使用的Texture會完全加載,即加載Mipmap 0~n級;如果Budget不足,則按Max Level Reduction加載。
(3)動態(tài)加載的GO Texture在Load和Instantiate時(在此時可能并未實際渲染該物體),Unity會始終首先加載其Max Level Reduction級的Mipmap到內(nèi)存中,這樣做的好處是加載速度會變快,因為只需要加載一個Mipmap等級,占用的內(nèi)存會少,另外Texture Streaming System會為其使用紋理異步加載。
(4)在我們實際需要渲染GO時(當(dāng)Instantiate GO后,我們可能需要立刻渲染該物體,或者該物體Active后出現(xiàn)在攝像機內(nèi)等等情況),CPU會按照當(dāng)前空閑的紋理串流預(yù)算和攝像機和物體之間的距離等等因素去計算當(dāng)前需要加載的Mipmap等級。如果Budget足夠,則加載計算出的Mipmap等級;如果Budget不足,則依然加載Max Level Reduction級別的Mipmap。
(5)在運行時,當(dāng)我們需要加載一個新的Texture且當(dāng)前紋理占用內(nèi)存超過了預(yù)算,Texture Streaming System會想辦法開始減少Texture占用的內(nèi)存。對于Scene自帶的所有GO,Unity會以距離攝像機從遠到近的順序重新計算來判斷其是否真正需要加載當(dāng)前其Mipmap等級,如果不需要則會卸載其過高的Mipmap等級,以此給出內(nèi)存空間給到新加載的Texture。此時,對于需要加載的新Texture,如果其計算出的Mipmap等級可以加載(即空閑內(nèi)存足夠)則加載其計算出來的Mipmap等級;如果不能加載(在按策略卸載部分GO不需要的Mipmap后,內(nèi)存還是不夠),則加載Max Level Reduction級別的Mipmap。從這一點也可以看出,對于一個Texture,其實際加載的最大Mipmap等級就是Max Level Reduction(即使會超出Budget也會加載這一等級)。
5. 幾個關(guān)鍵點
此外,UWA課堂中也提到了幾個使用Texture Streaming System的關(guān)鍵點,也算是他們踩過的坑。
(1)移動端一定要通過代碼設(shè)置QualitySettings.streamingMipmapsActive = true。如果只是在QualitySettings中手動勾選,則Editor下會起作用,但移動端可能會出現(xiàn)不起作用的情況。
(2)Unity版本升級后,可能會使紋理變糊。解決方法:開一個新項目,把所有Mesh、Texture原封不動拷貝過去。
(3)不要相信Editor Profiler,直接在真機上測試。這一點非常重要,在Editor下的紋理占用內(nèi)存量會比真機大得多(可能是使用的資源路徑不同),同一情況下,在Editor下紋理占用可能到達300MB,但在真機上可能只有30MB。因此一切Texture Streaming相關(guān)的測試一定要放在真機上進行!
(4)在不激活Texture.streamingTextureDiscardUnusedMips的情況下,Mip被丟棄的時機(指已加載的Mip從顯存中卸載)應(yīng)該只有一個,即當(dāng)前紋理串流預(yù)算不足,且需要加載新的Mip Streaming Texture,此時從遠到近查找不需要使用的Mip卸載。這也就意味著直接拉遠鏡頭并不會立即觸發(fā)Mip等級的降低,因為之前我以為拉遠了鏡頭就應(yīng)該使用模糊的Mip了,這一點比較重要,一開始以為是Bug。
6. Mipmap偏移MipmapBias
在實際項目中,我們可能需要針對不同性能的機型使用Mipmap偏移。要想達到Mipmap偏移有幾種方法:
(1)QualitySettings.masterTextureLimit:其默認(rèn)值為0,將其值設(shè)置為x,會對所有開啟Mipmap的Texture2D資源(不管是否開啟Mipmap Streaming)使用第x級Mipmap。
(2)Texture.mipMapBias:對于單個Texture設(shè)置其Mipmap偏移。
(3)Streaming Controller組件上的Mip Map Bias:此值只在QualitySettings.AddAllCamera未啟用,且在Camera上激活Streaming Controller時起作用。此設(shè)置會針對當(dāng)前攝像機需要渲染的Renderer其使用的Texture(開啟Mipmap Streaming的Texture)進行Mipmap偏移,比較推薦使用這一種方法。
在第三種方法中,MipmapBias的優(yōu)先級是低于MaxLevelReduction的,舉例來說如果當(dāng)前MaxLevelReduction=3,MipmapBias=2,計算出的Mip等級為2,理論上應(yīng)該使用Mip4,但因為MaxLevelReduction=3,所以最后會加載Mip3(即加載Mip3 ~ n到顯存中)。
接下來對三種使用方法進行測試。
對于方法1,其實際使用的Mip等級并不會反應(yīng)在Texture.loadedMipmapLevel上,Texture.loadedMipmapLevel返回的是串流系統(tǒng)當(dāng)前加載的Mipmap級別。通過QualitySettings.masterTextureLimit進行Mip偏移會將低等級Mip從顯存中卸載,達到內(nèi)存優(yōu)化的效果。測試結(jié)果如下圖所示。

對于方法2,其控制單個紋理的Mipmap偏移。該方法對開啟Mipmap的紋理起作用,與是否開啟Streaming無關(guān)。
對于方法3,它是在Mipmap Streaming System中對Mip進行偏移的,其使用條件比較繁瑣,但其也是最合理的一種使用方法,即只對需要的攝像機、需要的紋理進行開啟Mipmap偏移。對于方法3,因為涉及到了Mipmap Streaming System的管理測換,因此進行了比較詳細的測試。
首先測試不開啟MipmapBias的情況,進入測試場景,Budget為10,MaxLevelReduction為5,攝像機上的MipmapBias為0,目前已使用紋理內(nèi)存為7.5MB,低于Budget。

此時降低Budget到7(實際紋理占用內(nèi)存為8),觸發(fā)了Budget不足時卸載Mip的邏輯,此時wlop紋理的Mip等級變?yōu)榱?,即從顯存中卸載了Mip0。

接下來測試MipmapBias=2的情況,進入場景,MipmapBias調(diào)整到2,如下圖所示,雖然目前我們將MipmapBias調(diào)整到了2,但是wlop紋理使用的Mip等級依然是0,內(nèi)存也沒變化,因此調(diào)整MipmapBias并不會即時生效。

然后再將Budget降低到7觸發(fā)卸載邏輯,如下圖所示,wlop紋理的Mip等級變化為3(MipmapBias為0時變化為1),此時MipmapBias生效。

從這點看出,MipmapBias會在Mipmap Streaming System計算Mip等級的時候才生效(在Texture.streamingTextureDiscardUnusedMips開啟后會實時生效,在前文中提到過,開啟后Mip計算邏輯會每幀主動觸發(fā)),而計算Mip等級的時機也就是第4點Texture Streaming System管理策略里提到的那些。雖然說這里我是通過調(diào)整Budget來觸發(fā)的卸載邏輯,但至于真正項目里是否需要動態(tài)調(diào)整Budget網(wǎng)上也沒有相關(guān)人提到過(目前實際使用該系統(tǒng)的商業(yè)項目應(yīng)該也只占少數(shù)),因此不確定該方法能否實際應(yīng)用。
7. 調(diào)試紋理串流系統(tǒng)
在Built-in管線中,Unity原生提供了Mipmap Streaming的調(diào)試工具,具體可參考官方文檔。由于本文使用的是URP管線,無法使用Unity原生工具,因此自己參考官方提供的調(diào)試Shader手寫了一個簡單的Shader來用于調(diào)試。為了不在每個Shader中增加一個Pass用于調(diào)試,我選擇在運行時將所有材質(zhì)的Shader替換成調(diào)試Shader的方法,畢竟不用考慮性能(本來考慮RenderFeature的方法,比較好熱插拔,但是就關(guān)于如何獲取原材質(zhì)的MainTex問題難以得到解決,因此使用這種方法)。
在該調(diào)試用Shader中,由于不考慮性能問題,我寫得比較粗糙,用了很多if判斷,在實際運用中,大家可以根據(jù)需要自己編寫Shader使用。大致思路就是,首先每一幀需要執(zhí)行一個Texture.SetStreamingTextureMaterialDebugProperties(),通過每幀執(zhí)行該函數(shù),Unity會把每個材質(zhì)上使用到的開啟Mipmap Streaming的紋理的一些Mipmap信息(例如該紋理的Mip總數(shù)、當(dāng)前使用的Mip等級等)傳遞給著色器,在著色器中通過類似"_MainTex_Mipinfo"(寫法類似_MainTex_ST)來獲取。然后就可以在Shader里根據(jù)當(dāng)前Mip等級反應(yīng)出不同的顏色用于將Mip等級可視化了。
我寫的Shader代碼如下。



其效果如下視頻所示,未開啟Mipmap Streaming的紋理將會顯示為粉紅色,開啟Mipmap Streaming的紋理會根據(jù)其當(dāng)前使用的Mip等級呈現(xiàn)不同程度的綠色,綠色越鮮艷代表Mip等級越低。
8. 總結(jié)
目前Unity提供的Mipmap Streaming System是一項還算比較先進的技術(shù),如果使用得當(dāng)可以達到較好地降低紋理內(nèi)存、加快加載速度的效果,但是在使用的過程中會遇到很多坑,比如其參數(shù)與Texture自帶的一些參數(shù)關(guān)系較為混亂、Unity官方并未對其運作原理進行詳細解釋等,大部分時候只能靠我們?nèi)プ鰧嶒炄ゲ聹y其運行邏輯,算是比較難駕馭的一項技術(shù),因此也很少看到項目會使用到技術(shù)并分享該技術(shù)的使用方法(雖然說不同類型的游戲,其使用方法肯定不會相同,但是我仍未看到過一套比較成熟的使用方案)。目前我對Unity紋理串流系統(tǒng)的認(rèn)識依然比較淺薄,理解也算是比較淺層,也希望能有一些使用過這項技術(shù)的同學(xué)能分享一些自己的見解吧。
參考
The Mipmap Streaming system API
Unity3D研究院之給每個貼圖指定不同mipmap減低紋理帶寬(一百一十四)
淺談Unity中的Mipmap與Texture Streaming技術(shù)
沒弄懂的 Texture Mipmap Streaming (Unity 2018.2)
Unity引擎加載模塊和內(nèi)存管理的量化分析及優(yōu)化方法
圖片來自WLOP
這是侑虎科技第1348篇文章,感謝作者歐幾里得范數(shù)供稿。歡迎轉(zhuǎn)發(fā)分享,未經(jīng)作者授權(quán)請勿轉(zhuǎn)載。如果您有任何獨到的見解或者發(fā)現(xiàn)也歡迎聯(lián)系我們,一起探討。
作者主頁:https://www.zhihu.com/people/ou-ji-li-de-fan-shu-34
再次感謝歐幾里得范數(shù)的分享,如果您有任何獨到的見解或者發(fā)現(xiàn)也歡迎聯(lián)系我們,一起探討。