Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9309

【Unity】Texture2Dを毎フレーム更新する処理を書いたらメモリリークした

$
0
0

概要

ある GameObject の Texture を毎フレーム更新して動画のように描画するコードを書いていたところ、ネイティブ環境で異常にクラッシュする事象が発生した。その時に調査した手法と原因をまとめておく。

調査手法

下記のようなエラーを事前に吐いていたので、メモリ関連であるということは分かっていた。 (WebGL)

Uncaught RangeError : Maximum Call Stack Size Exceeded

次に Profiler からメモリ使用量を確認すると、Unity メモリが 3GB を超えていることを確認。(上昇ペースも1秒に数十MBだった)

image.png

更に、メモリビューの Detailed から何がメモリを食っているのか確認すると、画像のように Texture2D が毎フレームごとに 2.3MB メモリ確保していることが判明した。

image.png

Texture2D をこんなに生成している箇所は 1 箇所しか心当たりが無かったので、該当コードを確認することにした。

原因

該当コードは下記の通り。

// frameはtexture情報をバイナリで保持している独自クラスvartexture2D=newTexture2D(frame.Width,frame.Height,TextureFormat.RGBA32,false);texture2D.wrapMode=TextureWrapMode.Clamp;texture2D.LoadRawTextureData(frame.Buffer);texture2D.Apply();material.mainTexture=texture2D;

ここで問題になっているのは new Texture2D()の部分で、今回確保されたメモリは今フレームでは material.mainTextureが参照するが、material.mainTextureは次フレームでは別の参照を保持している。そのため、前フレームに確保した Texture2Dのメモリ領域はどこからも参照されなくなり、Unity メモリなので GC も行われずそのままリークするといった事が原因であった。

解決策

前フレームで使用していた Texture2Dのメモリを手動で解放する事で解決。
Unity で利用するアセットのメモリ解放は MonoBehaviour.Destroy()で出来る。
実際の修正済みコードは下記の通り。

vartexture2D=newTexture2D(frame.Width,frame.Height,TextureFormat.RGBA32,false);texture2D.wrapMode=TextureWrapMode.Clamp;texture2D.LoadRawTextureData(frame.Buffer);texture2D.Apply();// これを追加したDestroy(material.mainTexture);material.mainTexture=texture2D;

これで無事にメモリリークは解消され、ネイティブ環境でアプリがクラッシュすることも無くなった。

まとめ

「アプリが重い、メモリ関係でクラッシュする」といった場合はまず「Profiler」を確認すること。
闇雲にあたりを付けて修正してもコストが掛かるだけで改善するとは限らない。

Profiler を使った最適化は 「【Unite 2017 Tokyo】最適化をする前に覚えておきたい技術」の動画を見たメモにまとめてあるので、全く縁が無かった方は読んでおくと良いと思われる。

※ 調査方法や解決方法で更に効率的な方法などがあればコメントを頂けますと幸いです。


Viewing all articles
Browse latest Browse all 9309

Trending Articles