「デカいテクスチャはどれだ?」テクスチャのリストを吐いてみる

こんにちは。技術部平山です。

今回は小ネタです。プロジェクト内のテクスチャを見て、

5592405  1024x1024   RGB24   mips=11 faces=1 readable=False  AssetStore/[検閲]/texture/body-blue.psd
5592405 1024x1024   RGB24   mips=11 faces=1 readable=False  AssetStore/[検閲]/texture/body-yellow.psd
5592405 1024x1024   RGB24   mips=11 faces=1 readable=False  AssetStore/[検閲]/texture/body-pink.psd
5592405 1024x1024   RGB24   mips=11 faces=1 readable=False  AssetStore/[検閲]/texture/body-green.psd
5592405 1024x1024   RGB24   mips=11 faces=1 readable=False  AssetStore/[検閲]/texture/body.psd
5130300 1047x1225   RGB24   mips=1  faces=1 readable=False  [検閲]/Scenes/GameScene/Game/Sprites/bg_gradation_off.png
4872000 750x1624    RGB24   mips=1  faces=1 readable=False  [検閲]/Scenes/[検閲]/Sprites/[検閲].png
4872000 750x1624    RGB24   mips=1  faces=1 readable=False  [検閲]/Scenes/[検閲]/Sprites/_layout_point.png
4872000 750x1624    RGB24   mips=1  faces=1 readable=False  [検閲]/Scenes/[検閲]/Sprites/_layout_9box.png
4872000 750x1624    RGB24   mips=1  faces=1 readable=False  [検閲]/Scenes/GameScene/Game/Sprites/_layout_top.png
4872000 750x1624    RGB24   mips=1  faces=1 readable=False  [検閲]/Scenes/GameScene/Game/Sprites/_layout_ingame.png
4872000 750x1624    RGB24   mips=1  faces=1 readable=False  [検閲]/Scenes/[検閲]/Sprites/[検閲].png
4872000 750x1624    RGB24   mips=1  faces=1 readable=False  [検閲]/Scenes/[検閲]/Sprites/[検閲].png
4872000 750x1624    RGB24   mips=1  faces=1 readable=False  [検閲]/Scenes/TitleScene/Sprites/_layout_top.png
4375360 968x1130    RGB24   mips=1  faces=1 readable=False  [検閲]/Scenes/GameScene/Game/Sprites/bg.png
2796202 2048x2048   PVRTC_RGB4  mips=12 faces=1 readable=False  [検閲]/Scenes/GameScene/Game/Background/ingame_bg_3.png

みたいなリストを吐くスクリプトを書きました。スクリプトはGithubに置いてあります

なんで作った?

今やっている製品で、無駄に大きなテクスチャがないか調べたかったからです。 よくある話ですね。

使い方

スクリプトをどこかのEditorフォルダにつっこむと、メニューにKayac/MakeTextureListが出てきます。 そして実行すると、プロジェクトフォルダにtextureList.txtができます。

そのままテキストエディタで閲覧してもいいし、 スプレッドシートにつっこんでから見てもいいでしょう。 そのためにタブ区切りにしてあります。 ファイル名やフォーマット、サイズでソートあるいはフィルタリングしたいこともあるでしょう。

左端のサイズはバイトでして、「メモリ内で食うサイズ」を粗く計算して出しています。 スクリプトのCalcTextureByteSize をご覧ください。フォーマットごとに分岐していて長くなっていますが、やっていることは簡単だと思います (ASTCとBCはテストしてませんので、お使いの場合は注意してください)。

なお、終了後にログに総量が出るので、

f:id:hirasho0:20191120111416p:plain

粗く現状を把握したり、削減作業の効果を見たりするのも楽になっています。

どう役立てる?

今回の件では、AssetBundle化せず、全データをアプリ本体の配布に格納しますので、 サイズには制約があります。apkやipaが大きくなりすぎないようにする必要がありますし、 仮に制限に収まったとしても小さい方がお客さんにとっては良いでしょう。

さて、小さくしたいと思った時には、 「どのテクスチャを対象に削減作業をすればいいか」を知りたいわけですが、 こんな面白くない作業に手間はかけたくありません。

そこで、大きい順に並べて、上の方に出てくるテクスチャについて、 「サイズは適正か」「圧縮方式は適正か」 といったことをチェックすれば楽ですね。 ついでに、メモリを余計に使うのも嫌なので、「Readableがtrueになってないか」 も表示しておきました。

実際の例としては、2048x2048で無圧縮のテクスチャが4枚見つかりました。 これだけで64MBです。最大サイズを1024にし、不透明だったのでETC/PVRTC4bitに圧縮することで、 4MBに削減することができました。

また、ファイル名でソートして特定のフォルダ以下のものだけを合計することで、 「○○以下が容量の大半を占めてる」といったこともすぐわかります。スプレッドシートに入れてから見ると良いですね。 「この種別の最大テクスチャサイズを半分にすれば○○MBだけ減る」ということがわかっていれば、 実際にそれをやるかどうかの検討もしやすいでしょう。 「キャラ一体あたり1024x1024使ってて合計200MBあるけど、512x512でも許せるなら50MBになるよ。どうする?」 といった感じです。

また、「UIのスプライト類が圧縮かかっちゃってて汚ないけど、無圧縮にしたらどれくらい増える?」 というようなことも、同様の手順ですぐわかります。

なお、ゲームに含まれるデータはテクスチャだけではありませんが、 普通にゲームを作っていれば容量の大半はテクスチャでしょう。 もし音声データが多いゲームなのであれば、音でも同じことをやるのが良いと思います。

実装概要

まずAssetDatabase.FindAssetsでテクスチャを全部取ってきます。

var guids = AssetDatabase.FindAssets("t: texture");

返ってくるのはGUIDなので、これをAssetDatabase.GUIDToAssetPath でパスに変換し、AssetDatabase.LoadAssetAtPath でメモリにロードします。これでTextureを得てから、Texture2DなのかTextureCubeなのか(必要なら3Dなのか、2DArrayなのか等も)調べ、 必要な情報を抜きます。

幅、高さ、ミップマップ数、Cubeか否か、フォーマット、の情報がわかれば、 そこからバイト数を概算できます。「概算」なのは、実際にメモリの中でどれだけ使うかはハードウェアの設計次第で結構変わるからです (1x1でも最低4KB使う、みたいな制約は結構あります)。 ファイルに格納する際にも「それぞれのミップレベルは4KB単位のサイズに切り上げる」というような制限があるかもしれず、 実際のことは案外わからないものです。しかし、そんな細かいことはどうでも良いですね。

実用面での注意としては、時間がそれなりにかかるのでプログレスバーを出すのと、 途中でエラーが出た時にプログレスバーが出しっぱなしだと操作不能になるのでtry-catchでくくること、くらいでしょうか。

カスタマイズ

高さと幅が「512x512」のようにxでつながっているので、これをタブで分離すると、スプレッドシートで扱いやすくなるでしょう。 「Readable=True」みたいなのはTrueだけにした方がスプレッドシート向けでしょう。 今回はテキストエディタ主体で考えていたのでこうなっています。

また、使われていないファイルを外して表示した方が良いかもしれません。 今回それをしていないのは、「使われているか」の判定で待ち時間が伸びるのが嫌だったからですが、 便利な局面は多いでしょう。 実際、上のリストでは_layout_top.pngのようにいかにもビルドに入らなそうなファイルが 入ってしまっています。

もし実装する場合は、依存関係を調べる記事で使った手法 と同じ感じで行けると思いますが、結構な時間がかかることが予想されるので、 結果をうまくキャッシュしたり、他の用途にも使ったりすると良いかと思います。

おわりに

こういう小さいツールはどこでも作ってますよね。 もっと高度な方法で全体のアセット量を把握している所もあるでしょうし、 「そもそも把握する必要がない」というより進んだ手法を取っている所もあるかと思います。 その意味で、あまり価値はない記事です。

ところで、この手のツールはエディタ拡張に慣れるのに丁度いいネタです。 敢えて共通化せず、 それぞれのチームで未経験者に作らせるのもいいかもしれませんよ。 なんでもかんでも共通化、共有化してしまうと、「育成」という側面が抜け落ちてしまうこともありますから。