Texture2DArray

今回の主な話題：Texture2DArray、遠距離オブジェクトの物理シミュレーション、パーティクルシステムのCPU / GPUコスト、StreamingAssetsディレクトリの圧縮パッケージの読み取り方法。

---
###パーティクルシステム
Q1：パーティクルシステムのコストをどのように区別しますか？どの部分がCPUにあり、どの部分がGPUにありますか？ 空のパーティクル（レンダリングを含むすべてのモジュールを無効にする）にはコストがありますか？ あったら、どこに反映されますか？
>現在のUnityエンジンのネイティブ機能について、パーティクルシステムでのステータスの更新（パーティクルの位置、方向、トリガーイベントなど）はすべてCPU側のコストであり、具体的には、ParticleSystem.UpdateやParticleSystem.EndUpdateAllなどの関数で示されます。UWAパフォーマンスレポートで次の2つの画面をチェックできます。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/4e4e241d-b7da-4d5a-4424-e62d99f72e28.png)
[関するリンク](https://www.uwa4d.com/demo/pa.html?v=5x&platform=android&name=xmcs&engine=unity#particlesys)
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/f2a0b5cc-c321-8141-e710-1279519dfef4.png)
[関するリンク](https://www.uwa4d.com/demo/pa.html?v=5x&platform=android&name=xmcs#cpudetail)
同時に、 パーティクルシステムがレンダリング中に、CullingやDraw CallオペレーションなどのCPU側の準備も必要です。次の図に示すように、これらもCPUコストの一部です。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/19f53464-fd33-7bde-539c-cc9d5b51636d.png)
GPU側での時間コストは他のMeshレンダリングと同じで、主にALU、Bandwidth、Overdrawと関します。ただし、他の一般的なシーンのレンダリングと違いのは、GPU側のパーティクルシステムの圧力は常にOverdrawに反映されます。下図のように。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/77564f04-284b-2461-1f0e-09a84edade93.png)
上の画像は「キワメ」のパフォーマンスレポートからのものです。完全なデータを参照して、この部分の理解を深められます。空の粒子のコストは理論的には非常に小さいですが、UWAはまだそれをテストしていません。問題主は自分でテストできます。

---
###アセット管理
Q2: Unity 5.5バージョンを使っています。プロジェクトはLZ4のAssetBundleを使用しているため、StreamingAssetsに直接配置すると、パッケージが大きすぎになります。今は、すべてのAssetBundleを一つの圧縮パッケージにパックし、StreamingAssetsに入れて、プレーヤーが最初にインストールされたときに、PersistentDataPathディレクトリにコピーして解凍しますようにしたいです。しかし、AndroidプラットフォームのStreamingAssetsディレクトリにある圧縮されたパッケージの読み方がわかりません。ご指摘ください！
①
>直接にCを使ってunzipライブラリを実現しました。ゲームを始まるときに、Apkを開きます。後で、unzipライブラリで必要なファイルをキャッシュディレクトリに解凍し、読み取ります。読み取ったデータに対しで、必要がない場合は定期的にクリーンアップします。

②
>UnityのAPIでは、WWW（新しいバージョンのUnityWebRequestに対応）だけが非Bundleファイルにアクセスできますが、このインターフェースは非同期であります。さらに、.bytes操作は、より高いMonoメモリの割り当てを生成し、Monoメモリのピーク値を上昇させやすいです。だから、ある方法はAndroidのJava APIを介して読み取ることです。YusongMOMOさんが[この記事](https://www.tuicool.com/articles/fAnYJ3I)で使ったJavaインターフェースを参照できます。

---
###アセット管理
Q3: Unity 2017.2fバージョンを使用しています。PrefabのImage要素は、Sprite Atlasフォルダー内の画像を直接使用しています。今はPrefabとSprite Atlasを別々に違うAssetBundleにパッケージして、Sprite Atlasのinclude buildオプションも削除します。ツールでPrefabとSprite AtlasのBundleを確認し、Prefabが配置されているBundleはImage要素が使用したSpriteマップも中にパッケージします。また、Sprite Atlasが配置されているBundleには、Atlasマップが含まれるだけでなく、元の画像がすべてこのBundleに含まれています。これはどうしてですか？
>UWAがテストした後、Unity 2017.2と2017.3に問題主が述べた問題が存在していますと分かりましたが、一部の操作でこの問題を回避することもできます。まず、私が普通だと思うことについて話します。
include in buildはBundleのパッケージすることに影響ありませんはずです。そしてPrefabのBundleにSpriteAtlasの存在もずっとありませんはずです。ただし、include in buildを有効にしない場合、2つのBundleが正常にロードされ、Prefabがインスタンス化されると、late bindingを行う必要があるため、Spriteは表示されません。

>次に、発生したバグについて話しましょう。
１、Bundleを初めてパッケージするとき（する前にBundleディレクトリを空にする）、include in buildがオンにしますと、atlasはprefabのbundleにパッケージされます（問題主の問題のように）；
２、ただし、Bundleを初めてパッケージするとき、include in buildはオフの場合、それは正常です（AtlasはprefabのBundleにありません）。

>おもしろいのは、再びBundleをパッケージするとき、include in buildが開かれているかどうかに関係なく、結果が前回と同じであることです。だから、避ける方法は：     最初に、Bundleディレクトリをクリアし、include in buildを閉じてBundleを1回パッケージし、次にinclude in buildを開いて2回目にパッケージします。 その結果、AtlasはPrefabのBundleに含まれず、Atlasのinclude in buildがオンになります。

---
###レンダリング
Q4: [ドキュメント](https://docs.unity3d.com/Manual/SL-TextureArrays.html)によると、GLES3 Metalはすでにサポートされています。最も発想しやすいのは地形のスプラットレイヤーです。4層のスプラットの場合は、代わりにTexture2DArrayを一つ使用できます。利点はbindのコストを減らすことです。Splatのサンプリングの数も減らせるらしいですが、自分のテストと理解によるとダメです。Texture2DArrayのsliceはお互いにblendingしませんから。この理解は正しいですか？

Texture2DArrayを使用して、4層のスプラット地形を置き換えます。XCodeからコストを確認しますと、削減はありませんから、この方法で地形を作る意味は小さくなりました。AssetStoreにはMegaSplatと呼ばれるプラグインがあり、Texture2DArrayを使用して多くのレイヤーのミキシングを実現しています。頭に浮かぶTexture2DArrayを使用できるもう1つの場所は、シーンマップです。 たとえば、シーンで複数の1024テクスチャまたは複数のlightmapを使用している場合、StaticBatchingはテクスチャが異なるために合併できませんが、Texture2DArrayで実現できます。またはUIにあるiconみたいに、一つのTexture2DArrayに合併してDrawCallの合併を実現させられます。

現在のTexture2DArrayの最も不利な点は、コードでしか作成できないことです、エディターからのサポートはありません。Texture2DArrayをオフラインに作る場合は、異なるプラットフォーム用に複数のアセットを準備する必要があります。皆さんは共有できるTexture2DArrayに関する経験がありませんか？
>UWAはTexture2DArrayを了解しました、複数の2Dテクスチャを1つのオブジェクトに合併するようです。そして、一度だけバインドしますと複数の2Dテクスチャをサンプリングできます。確かにサンプリングする場合には、一回に確定なsliceを一つしかサンプリングできませんが、blendを完了するには別のShaderコードも必要です。

>問題主が「Splatのサンプリングの数も減らせるらしいですが、自分のテストと理解によるとダメです。」と言いました。彼が伝いたいのは「すべてのスライスを毎回収集する必要があるわけではなく、一部だけをサンプリングすることもできます。」と思います。元に表現したいのは「一回に一つのsliceしかサンプリングしません。」と思います。

>Texture2DArrayを使って4層のSplat地形を置き換えました。xcode から見ると、コストは減少してありませんから、現在では、こうやって地形を作る意味はあまり大きではありません。

>パフォーマンスだけから見ると、Texture2DArrayは実際にTexture2Dよりもテクスチャバインディングのコストだけを減らすことになり、ゲームエンジンのバッチ処理に影響を与える可能性があります。他のメソッドは通常のTexture2Dと同じです。Texture2DArrayはTexture3Dと比べてLOD処理に違います。Texture3Dはスライスを削減しますが、これはTerrainをレンダリングするときにやりたいものではありません。次に、Texture2DArrayはフィルタリング時にUとVでのみそれを行い、Texture3Dはdでも行うので、この部分もTexture2DArrayはTexture3Dよりもパフォーマンスが優れています。これらの要因がTerrainのレンダリングにTexture2DArrayを推奨する理由かもしれません。問題主は実験を通じて、Texture2DArrayレンダリングのTerrainがコストを削減しないことを示しました。これは、通常、1つのシーンに1つの地形があるのみの可能性もあります。4つのテクスチャを結び付けることと比べて一つを結び付けるがかかった時間は数ミリ秒だけ節約して、そして毎フレームにするではありませんので、効果は明ら​​かではありません。

>UWAは簡単な実験で説明します。下図はTexture2DとTexture2DArrayを使用して地形のレンダリング結果とGLES APIコールであります。設備はSamsung S6です。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/12fe03a5-e3e8-5ab9-ae29-d41557b2bd82.png)
Texture2D：
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/5a79cb86-98f4-347e-bf5e-b8f9e2c0e8a8.png)
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/4db79ee8-9859-feba-6ca4-c2615d7c848f.png)
Texture2DArray：
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/6615e0e5-e428-9f55-91ab-c1b91c4153eb.png)
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/71880265-701e-8b4d-4bc0-3b594b47131c.png)
このうち、GLES APIコールが赤枠でテクスチャバインディングAPIコールを表し、緑枠でレンダリングAPIコールを表します。WTは、APIコールの時間コストをナノ秒で表します。Texture2D図からすべてのテクスチャバインディングは5回でありことがわかりまして、材質の5回のテクスチャバインディングに対応します。しかし、Texture2DArrayには2回しかありません、splatテクスチャと4レイヤー混合テクスチャのバインディングに対応します。図から見られますのは、1回glBindTextureの時間コストはやく1000~10000ns、すなわち最大0.01ms。だから3回glBindTextureと3回glActiveTextureの時間を加えても最大0.06msで、簡単に見られません。

---
###物理
Q5：私たちのチームは一つの超巨大地形のMMOゲームを開発しており、シーンにいるキャラクターやマンスターが多いです。Unityエンジンは遠い場所（500Mまたは1KM以上）にあるGameObjectに物理模擬を行いますか？この部分の物理模擬コストを減らす設置はありませんか？
>オブジェクトがActiveであり、Rigidbody（Character Controller）をマウントすると、Sleeping状態にはなりませんなら、確かにUnityエンジンは遠い場所にあるGameObjectに物理模擬を行います。Unityエンジンには、距離に応じて物理システム を調整する計算はありませんが、自分でコントロールできます。例えば、Culling Groupまたは自分でGameObjectの距離を計算するでRigidbodyのActiveとDeactiveをコントロールして、全体的の物理システムの時間コストを節約します。

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

UWA公式サイト：https://jp.uwa4d.com
UWA公式ブログ：https://blog.jp.uwa4d.com