博客 / 詳情

返回

使用Streaming Mipmap後紋理內存沒有下降的疑問

1)使用Streaming Mipmap後紋理內存沒有下降的疑問
​2)TCP網絡傳輸大端/小端疑問
3)Texture Compression,Default和Override有什麼關係
4)如何快速清除Log


這是第299篇UWA技術知識分享的推送。今天我們繼續為大家精選了若干和開發、優化相關的問題,建議閲讀時間10分鐘,認真讀完必有收穫。

UWA 問答社區:answer.uwa4d.com
UWA QQ羣2:793972859(原羣已滿員)

Texture

Q:為什麼我在項目中使用了Streaming Mipmap但是在GOT報告中看紋理內存沒有下降?是沒有正確生效還是統計有問題?

A:之前做過相關測試,發現GOT Online是可以統計到被Streaming Mipmap影響的紋理的正確內存的,所以我推測你遇到的情況大概率還是沒有正確生效導致的。以下是對如何讓一張紋理應用Streaming Mipmap的簡單流程總結,其中會註明需要特別注意的一些條件(有些被官方文檔收錄,有些則文檔中沒有,但實驗證明為必要):

  1. 在Project Settings-Quality中開啓Texture Streaming選項。然而實驗發現Editor中開啓該選項而真機上卻會失效的現象,導致所有紋理的Streaming Mipmap設置全部失效。所以為了確保生效,首先應該在代碼中調用QualitySettings.streamingMipmapsActive API全局地開啓這個選項,才能確保Streaming Mipmap可用。
  2. 調整1中設置的參數。比較重要的參數是Memory Budget參數和Max Level Reduction參數。Memory Budget表示紋理資源的預算,默認值是512MB,但根據UWA的大量項目數據來看,中低端機上一般為200MB左右。它的數值代表的是所有紋理資源的預算——即,它既包括了非流式的紋理、又包括了我們想要採用流式的紋理——但這個“預算”並不代表紋理資源可佔用的上限,只是Unity判斷對於一個開啓Streaming Mipmap的紋理到底採用它的哪些Mipmap通道的參考值,非流式紋理能夠輕易突破這個預算。

Max Level Reduction則是代表着Unity通過流式存儲最高能取到哪一級的Mipmap通道,這個參數的優先級比Memory Budget要高,也就會造成實際內存超過預算的情況。(比如該參數為2時,則最多剔除Mipmap0和1通道,即便丟棄以後還遠超出預算值也不會進一步剔除。)

也就是説,如果Memory Budget值設置的遠高於項目中紋理實際佔用的內存,則Texture Streaming可能完全不起效,所有開啓Streaming Mipmap的紋理仍將保留它們的全部Mipmap通道。

  1. 設置開啓Streaming Mipmap的紋理。1、2中提到的設置只對開啓了Streaming Mipmap的紋理起效,而這只是官方文檔的説法,從實際操作來看,Texture Streaming只對同時滿足以下三個條件的紋理生效:

    1)開啓了Streaming Mipmap且開啓了Generate Mipmap的紋理(這一點官方文檔中沒有提及,事實上開啓Generate Mipmap才會生成Mipmap通道供Streaming Mipmap剔除);

    2)被即時加載的紋理(如一開始就已經在場景中被依賴的紋理,即便開啓了Streaming Mipmap其內存也不會發生變化,通過AssetBundle加載和Res.Load()加載的紋理則可以),也即官方文檔中這句話的實際含義:

    如果是進行Android開發,還需要打開Build Setting,並將Compression Method設置為LZ4或LZ4HC。Unity需要使用其中一種壓縮方法進行異步紋理加載,這是紋理串流系統所必需的操作。

    3)Gfx部分內存(這裏指的是紋理資源開啓Read/Write選項時,複製到CPU端的那一部分內存是不受Streaming Mipmap影響的);

  2. Streaming Mipmap剔除Mipmap通道的規律。它的機制其實和Texture Quality是類似的。我們知道,開啓Mipmap的紋理之所以會變成原來的4/3倍,實際上是它各個通道所佔用的內存之和。舉例而言,一個具有11個Mipmap通道的原大小為1MB的紋理(10241024分辨率、ASTC44格式),其內存佔用為1+1/4+1/16+…的11項等比數列之和,即約4/3。等比數列的各項就對應了Mipmap0、Mipmap1、Mipmap2…等各個Mipmap通道。那麼,當Max Level Reduction參數設置為2時,其實際意義就是保留Mipmap2和後續所有更小的通道,而剔除Mipmap0和Mipmap1通道,此時的內存大小為4/3MB-1MB-1/4MB=85.33KB。這和我在Profiler或GOT Online中看到的數據基本一致。
     title=
  3. 關於採用Streaming Mipmap方案的建議。根據上述實驗和分析不難看出,Streaming Mipmap是確實具有一些優點的,對於對內存比較敏感,尤其是紋理內存佔用很大的項目,採用Streaming Mipmap方案是非常合理和推薦的選項。與此同時,它的實際使用要求對項目中紋理資源的內存佔用有相當的瞭解和規劃——相關設置在Quality中,理所當然地應該考慮到不同設備Lod分級時不同的設置。在中低端機中,設置儘量低的Memory Budget和儘量高的Max Level Reduction;在高端機上則恰恰相反,在內存可接受範圍內儘量開啓最好的畫面表現。除此之外,對於哪些紋理要開啓Streaming Mipmap,一般是場景中3D物體的紋理,而UI模塊採用的紋理則儘量關閉。因為Mipmap的意義主要在於適應紋理在距離鏡頭遠近時的表現需要、避免失真等,而UI完全不需要這些,開啓只會白白浪費內存和計算時間。

感謝Faust@UWA問答社區提供了回答

Network

Q:網絡協議規定統一使用大端數據傳輸,但是測試用小端數據發送,然後服務器收到直接返回給客户端,客户端可以正確解析。所以就有個疑問:數據傳輸不需要大小端轉換?還是底層幫忙轉了?

測試例子如下:消息頭部固定4個字節,值為body的長度。服務器沒做任何處理收到消息直接返回。客户端收到消息先解析頭部長度4個字節,拿到body長度再獲取body具體數據。沒有大小端轉換但是解析都正確:

{
        // 測試發送整數
        var bodyMsg = "測試發送數據";
        var bodyBytes = Encoding.UTF8.GetBytes(bodyMsg);

        // 發送頭部長度,固定4個字節
        int headLen = bodyBytes.Length;
        var headBytes = BitConverter.GetBytes(headLen);

        // 發送頭部長度消息
        _sendSocketAsyncEventArgs.SetBuffer(headBytes, 0, 4);
        _socket.SendAsync(_sendSocketAsyncEventArgs);

        // 發送body消息
        _sendSocketAsyncEventArgs.SetBuffer(bodyBytes, 0, bodyBytes.Length);
        _socket.SendAsync(_sendSocketAsyncEventArgs);
}

A:測試發送的數據只有在多字節數據處理時才需要考慮字節序,如果是Protobuf,是不用考慮字節序的。

底層不會自動進行大小端轉換,需要寫代碼,所以一般都是項目初期就約定好。<br/>
另外,如果把服務器放到不同物理機則測試結果就是錯掉的,正確做法是得把發送數據轉為大端,比如上面消息頭要加上 if (BitConverter.IsLittleEndian) { Array.Reverse(headBytes); } ,然後在收到數據後再轉為小端。

感謝蕭小俊@UWA問答社區提供了回答

Texture

Q:請問紋理的格式設置項,BuildSettings裏面Texture Compression,Default和Override,這幾個是什麼關係?

 title=

A:紋理設置中Override For Android這一項的優先級最高;不勾選Override的情況下是Default中的格式。如果既沒有勾選Override,也沒有改變Default標籤下的格式設置,並且修改了Texture Compression選項,才會採用Texture Compression的格式設置,並且也只對符合上述條件的紋理生效。

感謝宗卉軒@UWA問答社區提供了回答

Script

Q:我們項目開發過程中使用Log還是比較多的,測試的時候發現對性能影響比較大、所以想在下版本中排除掉Log再看看性能數據。有沒有比手動註釋Log更好的方法?另外Release版本還會有Log嗎?

A1:可以自己封裝一個Log函數,Log函數內部再代用Debug.Log。可以在Void上加上[Conditional(“DEVELOPMENT_BUILD”),Conditional(“UNITY_EDITOR”)],這樣只有Debug版本和Editor才會調用。

感謝蕭小俊@UWA問答社區提供了回答

A2:補充一下,較新版的Unity中提供了接口Debug.unityLogger.logEnabled = false,放在打Log的邏輯之前就能避免所有Debug類觸發的所有Log了。另外Release版本是無法清除Debug.Log的,還是要嘗試用樓上或者API的方式去清除。

感謝Faust@UWA問答社區提供了回答

封面圖來源於網絡


今天的分享就到這裏。當然,生有涯而知無涯。在漫漫的開發週期中,您看到的這些問題也許都只是冰山一角,我們早已在UWA問答網站上準備了更多的技術話題等你一起來探索和分享。歡迎熱愛進步的你加入,也許你的方法恰能解別人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官網:www.uwa4d.com
官方技術博客:blog.uwa4d.com
官方問答社區:answer.uwa4d.com
UWA學堂:edu.uwa4d.com
官方技術QQ羣:793972859(原羣已滿員)

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.