Texture Streaming Pool Sizeの決め方を考えてみる

(本ブログはUE4.22を基に調査しております)

時々、 「Texture Streaming Pool」ってどれくらいにすればよいでしょうか？という質問をいただきます。ゲームにより様々な仕様がありそれぞれに適した調整方法があるかとは思いますが、今回は簡単である程度汎用的な調査方法を自分なりに考えてみました。「もっと良い物がある！」「俺はこうやった！」と言う情報があればウェルカムなのでぜひぜひ教えて頂ければ幸いです！！また、今回は**「そのシーンに合わせたPool Sizeを設定する」ということにフォーカスしています。「このシーンのテクスチャ使用量を減らしたいんだけど。。。」というのはまた別の話なので、その点ご了承ください。**

# Texture Streaming 説明の前に。。。
Texture Streamingとはという基本的な説明は省きますが、具体的な説明の前にいくつか共有できればと思います。

- Texture Streaming Pool Sizeは *r.streaming.poolsize* で設定。__動的変更可能__なので、シーンによって値を使い分けることができる
-  **必ず実機で確認する。** Texture Streamingによるメモリ消費やPoolの使い方は、プラットフォームによって異なります。必ず実機で確認をお願いします
- **StreamingされるTexture用のPool容量は *stat streaming*で表示される *Streaming Pool*を参照する。**(プラットフォームによって、r.streaming.poolsizeで確保したメモリ領域にNon-Streamingなtextureが入る場合と入らない場合があるのですが、最終的なPool用のメモリは上記*Streaming Pool*の値になる
- メモリが十分に足りてたら正直どんぶり勘定で良い。**無意味に目くじらを立てない。**

上記を押さえて、調整の方法を提案できればと思います。

# Texture Streaming Pool のSizeの設定手順

様々なカメラアングルやシーンで必要なテクスチャプールサイズは大きく異なるはずです。ですので、色々なシーンで以下の手順を試してみて最終的なPool Sizeを決定します。(statが必要なので計測はDevelopment Buildで！)

## 1. 前準備: ストリーマのキャッシュ機能をオフにする
   UE4のストリーマは、設定されたPool Sizeに余裕があればある程度キャッシュしてくれます。しかしメモリにあるTexture Sizeを厳密に知りたいときに邪魔になる可能性もあるので、一旦オフにします。

```
r.streaming.dropmips 1
```

## 2. "Pool Sizeに関係なく"ストリーマが本来必要としているメモリ容量を知る
 *Stat Streaming* の*Required Pool*や*stat streaming overview*の *Required*を見て、そのタイミングでストリーマがどれだけのメモリを必要としているのかを知ることができます。ここで重要な点は、この値は**Pool Sizeに関係がない**ということです。ストリーマが純粋にそのタイミングその位置でストリームされる全てのテクスチャの適切なMipを計算し、その合計を返してくれます。

__Stat StreamingのRequired Pool__
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363464/9810135e-143d-3f68-aa58-ea197075b08b.png)
__Stat StreamingOveviewのRequired 又は Average Required PoolSize__
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363464/1cddaf91-16a5-4a36-84ea-1517244823f5.png)
Average Required PoolSizeはそのシーン内を動き回っていたときの平均値ですので、これもシーン全体の傾向を知るのに役立ちます。


## 3. Required Poolのサイズに不安を感じたら。。不要なTextureやMip読み込みがないか確認する
"ListTextures streaming"で、Streamingされているテクスチャがどんなものかチェックすることができます。  ここで大きなサイズのテクスチャがメモリに乗っていたり不要なものが入っている場合、その原因を調査し削減できないか調査してみると良いと思います。(例えば、本来は非常に遠くに見えるテクスチャで解像度の低いMipMapで十分なのに、そのテクスチャが間違えて2のべき乗ではなかったりして、MipMapが作られておらずそのテクスチャの最大解像度のままメモリに乗ってたり。。。などなど。話はそれますが、2のべき乗じゃないテクスチャはMipがないので最初のテクスチャロードで最大解像度が読み込まれロード時間にも影響しますのでご注意を)

```
ListTextures streaming
```

**ListTexture stremaingの一例** 

```
Listing streaming textures.
MaxAllowedSize: Width x Height (Size in KB, Authored Bias), Current/InMem: Width x Height (Size in KB), Format, LODGroup, Name, Streaming, Usage Count
2048x2048 (5461 KB, 0), 2048x2048 (5461 KB), PF_DXT5, TEXTUREGROUP_World, ...T_LV_Soul_grass_D.T_LV_Soul_grass_D, YES, 29
2048x2048 (5461 KB, 0), 2048x2048 (5461 KB), PF_DXT5, TEXTUREGROUP_World, ...T_LV_Soul_BaseDirt_D.T_LV_Soul_BaseDirt_D, YES, 29
2048x2048 (5461 KB, 0), 2048x2048 (5461 KB), PF_DXT5, TEXTUREGROUP_World, ...T_Slums_TinRoof_D.T_Slums_TinRoof_D, YES, 137
2048x2048 (5461 KB, 0), 2048x2048 (5461 KB), PF_BC5, TEXTUREGROUP_WorldNormalMap, ...T_WaterStream0002_2_L_alpha2_N.T_WaterStream0002_2_L_alpha2_N, YES, 3
2048x2048 (5461 KB, 0), 2048x2048 (5461 KB), PF_BC5, TEXTUREGROUP_WorldNormalMap, ...T_Cave_Rock_Large02_N.T_Cave_Rock_Large02_N, YES, 113
2048x2048 (5461 KB, 0), 2048x2048 (5461 KB), PF_BC5, TEXTUREGROUP_WorldNormalMap, ...T_Slums_Metal_Rust_03_N.T_Slums_Metal_Rust_03_N, YES, 282
...
```


##4 Non-Streamingのテクスチャも確認

ストリーミングされないTextureらによって、過剰にStreaming Poolが小さくなる場合もあります。*ListTextures nonstreaming*を見て不必要なものが過剰にないか確認してみます。

```
ListTextures nonstreaming
```

## 5. Streaming PoolがRequired Poolの容量を安全に満たす様、r.streaming.PoolSizeを設定して歩き回る
手順3や4で適切なRequired Poolやnonstreming mipsの容量が得られたため、その値を満たすようにPoolSizeを設定します。

```
r.streaming.poolsize XXX
```

これで、静止した状態としては、必要なテクスチャが読まれずボケボケ。。。などということは起きずかといって不必要に大きなPoolSizeをとっているわけでもない適切な状態になっているかと思われます。

ここからは、動的にシーンを動かした際にどれだけテクスチャストリーミングが間に合わないかの検証となります。 ストリーマのキャッシュ機能を戻して、シーンを歩き回ってみて、致命的にストリーミングが間に合わない場所などがあればそこを見つけます。

```
r.streaming.dropmips 0
```

## 5. ストリーミングのスピードが改善できるか確認する
もしもストリーミングが間に合わない場所などがあれば、まず、その遅れるテクスチャのTexture Groupなどが適切に設定されているかを確認します。Texture GroupがStreamingに与える影響は前回記事にさせていただきましたのでそちらを参考にして頂ければ幸いです。 
[Texture GroupがTexture Streamingに与える影響](https://qiita.com/EGJ-Nori_Shinoyama/items/8b3c2e4e372f05e33476)
 
それでも改善しない場合は少しだけpool sizeを上げてみてください。初回は間に合わなくてもそれ以後はキャッシュの機能で若干改善する可能性があります。

(また、CPU負荷を分散させるため、ストリーマは1フレーム内で確認するメッシュの量を調整しています。この値を大きく取ることで、Streamingの遅延が改善したとの例を伺っています。ただ、CPU負荷が上がりますので、こちらを行う際は処理負荷の確認を忘れずにしてみてください。)

```
r.Streaming.NumStaticComponentsProcessedPerFrame
```

それでもそれでもそれでも改善しない場合は、様々な手法でストリーマにそのテクスチャを早く読ませる様に工夫する必要がありますが、今回の記事ではそこは言及しません。ただ重要なのは、こうなるとストリーマのストリーミングのスピードと優先順位が問題の本質となり、**いくらストリーミングのPool Sizeを大きくしても意味がない**ということです。テクスチャストリーミングの問題では、メモリの量と読み込みのスピードを分けて考える必要があります。

#おわりに

以上、簡単ですがTexture Streaming Pool Sizeを決め方を考えてみました。色々なユースケースによってもっと具体的な調査が必要なときは多々あるかと思いますが、最初の一歩になれば幸いです。