Unityローディングモジュール詳細分析——Shader編

前回の記事[「Unityローディングモジュール詳細分析——Mesh編」](https://blog.jp.uwa4d.com/2021/07/08/unity%e3%83%ad%e3%83%bc%e3%83%87%e3%82%a3%e3%83%b3%e3%82%b0%e3%83%a2%e3%82%b8%e3%83%a5%e3%83%bc%e3%83%ab%e8%a9%b3%e7%b4%b0%e5%88%86%e6%9e%90-mesh%e7%b7%a8/)で、メッシュアセットのローディングパフォーマンスについて話しました。今回はShaderアセットのローディング効率について説明します。

---
##アセットローディングパフォーマンステスト用コード
前の記事で提出したテストコードと同様に、Shaderアセットのローディングパフォーマンス分析にもこのテストコードを採用します。各Shaderファイルを特定サイズのAssetBundleファイルに作成し、下記のコードを介して異なるデバイスに1つずつローディングを行います。異なるデバイスのアセットローディングパフォーマンスを取得して比較します。
![1-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/907f00b2-c0c7-b205-2bae-c4680a393f09.png)
###テスト環境
エンジンバージョン：Unity5.2バージョン
テストデバイス：グレードの異なる3つのモバイルデバイス（Android：Redmi 2、Redmi Note2、Samsung S6）

---
##Shadeアセット
Shaderアセットは、以前のMeshアセットやTextureアセットとは異なり、自身の物理的なサイズが小さいです。一般的に、1つのShaderアセットの物理sizeはただ数kbであり、メモリの中でも数十kbです。ですから、Shaderアセットの効率ローディングボトルネックは、それ自身サイズのローディングではなく、Shader内容の解析にあります。したがって、この記事で、UWAは皆さんがプロジェクトに最もよく使う4つのShaderアセットを選択し、Shaderアセットローディングの具体的な消費を皆さんに紹介してあげます。

---
###テスト1、各種Shaderアセットのローディング効率テスト
現在のプロジェクトで最も使用されている4つのshaderアセット、つまりMobile-Diffuse、Mobile-VertexLit、Mobile-Bumped Diffuse、およびMobile-ParticlesAdditiveを選択しました。4つグループのメモリ使用量はそれぞれ93KB、115KB、110KB、および6.9KBであり、対応するAssetBundleサイズは10KB、7KB、12KB、および3KB（LZMA圧縮）です。

これらのshaderアセットを3つの異なるグレードのモデルにロードします。偶然性を減らすために、各デバイスでロード操作を10回繰り返し、平均値を最終的なパフォーマンスオーバーヘッドとして採用します。具体的なテスト結果を次の表に示します。
![2-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/75c37a15-a6ee-ebc4-c97b-aa3221bde688.png)
上記のテストを通じて、下記の結論を得ることができます。
>1、Shaderアセットの物理体積とメモリ使用量が小さいですが、ローディング時間コストが消費するCPU占有が高いです。これは、Shaderの解析CPUコストが高いためです。Shaderアセットローディングのパフォーマンス ボトルネックになります。
2、Mobile / Particles Additiveの分析時間は、Mobile / Diffuse、Mobile / Bumped Diffsue、さらにはMobile / VertexLitよりもはるかに短いです。
3、Mobile / Particles Additive以外、他の3つの主流のShaderは、ロード時に大幅なフレームドロップを引き起こしたり、ジャムしたりします。ですから、開発チームは出来る限りにシーンを切り替えていない時点でShaderの事前ロード操作を行います。
4、ハードウェアデバイスのパフォーマンスが向上するにつれて、解析効率の違いはますます明白ではなくなります。

---
###テスト2、Mobile Shader vs. Normal Shader
UWAパフォーマンス テストレポートで、プロジェクトにMobile Shader以外にDiffuse、Bumped DiffuseなどのShaderも大量に使われています。両者がレンダリング方面も違いは皆さんがすでに知っているはずですが、ローディング方面も一定のパフォーマンス の違いがありませんか？

これに対して、下記の実験をしました。テスト1のShaderアセットに基づいて対応するNormal Shaderを追加し、3つのテストデバイスでローディング操作を10回繰り返し、平均値を最終的なパフォーマンスオーバーヘッドとして採用しました。具体的なテスト結果を次の図に示します。
![3-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/eb190ae5-4532-568b-d34b-ea24ccf8d975.png)
![4-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/6ebbbd82-a625-eeb3-f1a4-387069baa374.png)
上記のテストを通じて、次の結論を取得できます。
>1、Mobile Shaderは、同じ種類のNormal Shaderと比べて、確かにローディング方面に一定のパフォーマンスの向上があります。
2、デバイスのパフォーマンスが低いほど、パフォーマンスの違いが大きくなります。例えば、Mobile/Bumped DiffuseとBumped Diffuseのローディングパフォーマンス の差はRed Mi 2で30msを達しました。

ここまで見れば、ほとんどの読者は驚かれることでしょう。あんな小さなShaderでありますが、ローディング用の時間コストはいくつかのAtlasテクスチャまたは面数万以上のMeshより長いですか？！はい、そうです。Shaderのローディング用コストは常に数百ミリ秒または数千ミリ秒以上です。

UWAのパフォーマンス評価レポートでは、多数のプロジェクトのShaderローディングは高いパフォーマンス消費を占めていることがわかりました。下図はあるプロジェクトが実行中のShaderローディング時間消費状況です。簡単に見られますのは、シーンが切り替えている時、ShaderローディングのCPU使用量は数百ミリ秒になります。一般的なダンジョンをプレイする時も数十ミリ秒のCPU時間が伴うことがわかります。これらは、実行中のフレームレートとシーンの切り替え効率に大きな影響があります。
![5−1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/225fc9a6-008e-d89e-74da-3d4ba50f9b76.png)
###`では、どうすればそれを最適化できますか？`
最適化の前に、最初に行う必要があるのは、Shader解析中に実際の時間かかる原因を理解することです。一般的に、ShaderローディングのCPU時間コストはKeyword数に関係あります。Keywordが多いほど、ローディングのオーバーヘッドが大きくなります。

Unity 5.xのInspectorを介して下記の内容を見られます。
Mobile/Bumped DiffuseのKeyword変数の量は39であり、
Mobile/DiffuseのKeyword変数の量は27であり、
Mobile/VertexLitのKeyword変数の量は15であり、
Mobile/Particles AdditiveのKeyword変数の量は1であります。

同様に、Unity 4.xでは、
Mobile/Bumped DiffuseのKeyword変数の量は44であり、
Mobile/DiffuseのKeyword変数の量は25であり、
Mobile/VertexLitのKeyword変数の量は6であり、
Mobile/Particles AdditiveのKeyword変数の量は0であります。

これもなぜMobile/Particles Additiveの解析オーバーヘッドがそれほど低い原因です。
![6.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/7d50ede3-f5a4-c84a-da31-16dc998dba32.jpeg)
**注意：ShaderのKeyword数はシーン設置の違いによって異なります。Unity 5.xで、Unityはデフォルトでシーン設定やShader Passなどに応じてShaderのKeywordを調整します。例えば、Lightmapが採用している場合、対応するKeywordがデフォルトで開かれます。Fogが使用していないプロジェクトに対して、関するKeywordは直接閉じられます。**

Shaderローディング効率に対して、Keywordの重要性を理解した後、ShaderのKeyword数を減らす方法を見つける必要があります。これに対して、開発チームに下記の方法をお勧めします。

---
####方法1：
Unity 5.xのプロジェクトに対して、skip_variants操作を介してShaderの中に関連Keywordを直接削除できます。

たとえば、Unity5.2バージョンで、Mobile / VertexLitのキーワードの数はデフォルトで15であり、次の図に示すようです。Shaderフラグメント#1には8つのKeywordがあることがわかります。これに対して、対応するShaderコードにskip_variants操作を追加してそれらを削除できます。削除後、Mobile / VertexLitのKeyword数は8になり、元々の8つKeywordはもう使用しなく、1つのデフォルトKeywordのみが残ります。
![7.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/5bf18c50-d635-6c98-34f6-bddf065fd996.jpeg)
![8.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/b5d31801-20c0-f9cc-4dbe-65bc002e2dc3.jpeg)
**以上から見られるのは、この方法はKeyword数を効果的に減らすことができますが、一定の制限もあります。1つ目は、現在のskip_variants操作がUnity 5.0以降でのみ使用できることです。2つ目はこの方法を使用するために、開発チームがShaderにある程度の理解が必要で、プロジェクトの実際な状況に応じてShaderを変更する必要があります。**

---
####方法2：
ShaderにあるFallbackオプションを直接削除します。Fallback機能とは、現在のShaderを使えないハードウェアデバイスに対して、ハードウェアデバイスへの要求がより低いFallback Shaderでレンダリングし、レンダリングの安定性を確保することです。しかし、現在のモバイル市場に対して、Mobile/DiffuseとMobile/Bumped Diffuseをサポートしていないデバイスはあまりありません（言い換えで、今までデバイスがMobile/Diffuse Shaderをサポートしないというフィードバックに遭うことがありません）。ですから、Mobile Shaderを採用するプロジェクトに対して、Keyword数を大幅に減らすためにFallBack機能を直接削除することができます。私たちのテストプロジェクトでは、FallBack機能を削除すると、Mobile/Bumped ShaderのKeywordが39から12に下がり、Mobile/DiffuseのKeywordが27から12に下がりました。
![9.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/fe7d4c87-a209-f994-9c40-4d771b2f2d7f.jpeg)
**この方法は、「方法1」のように「役に立たない」Keywordを完全に削除することはできませんが、この方法はシンプルで使いやすく、1つの手順で済むため、効率が高くなります。同時に、この方法はUnity 4.xエンジンのプロジェクトを完全にサポートしています。**

ここまで読んだら、間違いなく質問があるでしょう。つまり、Keyword数が減らせますが、Shaderの解析効率にどのぐらい向上できますか？これに対して、下記のような実験を行いました。

---
###テスト3：Fallback機能のON/OFFローディング効率テスト
簡単にするために、Mobile/Bumped DiffuseとMobile/DiffuseのFallback機能を直接オフにして、比較データを作成します。Fallbackを閉じた後、2つShaderのKeyword数は両方12でありますが、元ShaderのKeywordは39と27であります。

テスト1と同じように、3つの異なるグレードのAndroidモデルでロード操作を10回繰り返し、平均値を最終的なパフォーマンスオーバーヘッドとして採用しました。 具体的な試験結果を下図に示します。
![10-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/3fde6b1c-2df4-f3f6-532f-bf27f8f0df1a.png)
上記のテストで見られますのは、Keywordの削減により、Shaderの解析時間は確かに大幅に短縮でき、ローディング効率を向上することもできます。

---
##ローディング方式
上文で、具体的な実験を通じて、Shaderのローディングパフォーマンス 、時間のかかる原因、および最適化方法を示して分析しました。本物のプロジェクト開発過程中には、もう1つ注意しなければならないものがあり、Shaderのローディング方式です。下図に示すShaderのローディング状況のように、毎回シーンを切り替えると、Shaderのローディングが大量なCPU時間コストを占めます。実際には、これは同じShaderがたくさん繰り返し分析することが引き起こします。その原因は、Shaderが異なるAssetBundleファイルにパッケージ化され、毎回シーンを切り替えると、AssetBundleは頻繁にロードおよびアンロードされるため、同一Shaderがたくさん繰り返しロードおよびアンロードされます。
![11-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/4553a2d6-5613-167c-2e34-1546e7aff5d3.png)
上記の問題に対して、もしあなたがAssetBundleを使用してアセットをロードしているなら、推奨されるロード方法は次のとおりです。
>1、依存関係パッケージ化により、プロジェクトにあるすべてのShaderを分離して、1つの独立AssetBundleファイルにパッケージし、他のAssetBundleはそれに依存します。
2、ShaderのAssetBundleファイルは、ゲームが起動するとローディングを行い、メモリに駐在します。1つのプロジェクトにあるShader種類は一般的に50~100であり、毎個体は大きくないために、全部がメモリに駐在しても、合計メモリ使用量は2MBを超えません。
3、後続のPrefabがロードされてインスタンス化された後、Unityエンジンは、ロードおよび解析操作の代わりに、AssetBundle間の依存関係を通じて対応するShaderアセットを直接見つけて使用します。

**注意：Unity4.xバージョンに対して、ShaderのAssetBundleがロードした後に、LoadAllのみですべてのShaderのロードと分析を完了できます。しかし、Unity5.xバージョンの場合、LoadAllAssets操作を執行以外に、Shader.WarmupAllShaders操作を行う必要もあります。なぜなら、Unity5.xバージョンで、Shaderの解析とCreateGPUProgram操作は分かれていますから。**

Unity5.xを使用し、そしてアセットをロードするためにResources.Loadを使用している開発チームに、ShaderVariantCollectionを使用してShaderにPreloadを行うことをお勧めします。同じに同一Shaderのロード繰り返しを避ける効果を達すことができます。

上記のテストと分析により、Shaderアセットに管理にUWAのアドバイスは下記のように、
>1、レンダリング効果とプロジェクト要件を確保するという条件の下で、出来る限りにShaderのKeyword数を減らし、Shaderのローディング効率を向上します。
2、簡単なShaderに対して、Fallback操作を削除してみることができます。この方法は、現在がたくさん使用されているMobile/Diffuse、Mobile/Bumped DiffuseなどのBuilt-in Shaderによく適しています。
3、可能な限り、Shaderを単独な依頼関係パッケージ化し、そしてプリロードして、その後の不要なロードのオーバーヘッドを削減します。

以上は、Shaderアセットがロードする時のパフォーマンステストです。ローディングモージュンのパフォーマンス 問題について、UWAはボイス/アニメーションクリップなどの他のアセットのローディングパフォーマンス分析、アセットアンロードパフォーマンス分析、アセットインスタンス化パフォーマンス分析、異なるローディング方式のパフォーマンス分析などのシリーズテクノロジー記事を引き続き投稿し、今までテストしたプロジェクトの共有問題をまとめて紹介します。皆さんがローディング効率によく深い認識やローディングモージュンへの制御力を改善することを祈っています。

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

今なら、UWA GOTローカルツールが**15日間に無償試用**できます！！
よければ、ぜひ！

UWA公式サイト：https://jp.uwa4d.com
UWA GOT OnlineレポートDemo：https://jp.uwa4d.com/u/got/demo.html
UWA公式ブログ：https://blog.jp.uwa4d.com