Unity を検索

Unity 2021 LTS におけるシェーダーのビルド時間とメモリ使用量の改善

2022年12月28日 カテゴリ: Engine & platform | 13 分 で読めます
Improvements to shader build times and memory usage in 2021 LTS | Hero image
Improvements to shader build times and memory usage in 2021 LTS | Hero image
シェア

Is this article helpful for you?

Thank you for your feedback!

Unity のスクリプタブルレンダーパイプライン(SRP)で使える機能セットが増え続けるにつれ、ビルド時に処理されコンパイルされるシェーダーのバリアントの量も増えています。さらに、グラフィックス API への対応や、ターゲットプラットフォームの拡大など、SRP の進化はとどまるところを知りません。

シェーダーは、最初のビルド(「クリーン」ビルド)の後にコンパイルされキャッシュされるため、その後のインクリメンタルビルド(「ウォーム」ビルド)が加速されます。通常はクリーンビルドに最も時間がかかりますが、ウォームビルドにかかる時間がプロジェクトの開発やイテレーションにおける問題点となることがよくあります。

Shader variant processing and compilation during project build
プロジェクトのビルドにおけるシェーダーバリアントの処理とコンパイル

この問題に対処するため、Unity の Shader Management チームは、有意義でスケーラブルなソリューションを提供するため、懸命な取り組みを続けています。この取り組みの結果、Unity 2021 LTS 以降のバージョンを使用して作成されたプロジェクトのシェーダービルド時間および実行時のメモリ使用量が大幅に削減されました。

これらの新しい最適化について、影響を受けるバージョン、バックポート、内部テストで得られた数値などの詳細に関心がある方は、この後をスキップして、シェーダーバリアント の事前フィルタリングおよび動的なシェーダー読み込みのセクションをお読みください。このブログ記事の最後では、プロジェクトのオーサリング、ビルド、実行時の全体にわたって、シェーダーバリアント管理をさらに洗練させるという私たちの将来の計画についても述べています。

Unity のシェーダーシステムに施されたエキサイティングな改良を掘り下げる前に、条件付きシェーダーコンパイルシェーダーバリアントシェーダーバリアントストリッピングの概念も簡単に復習しておきましょう。

条件付きシェーダー機能

条件付きシェーダー機能により、開発者やアーティストはスクリプト、マテリアル設定、プロジェクトおよびグラフィックス設定を使用して、シェーダーの機能を簡単に制御および変更することができます。このような条件付き機能は、プロジェクトのオーサリングを簡素化し、オーサリングやメンテナンスを行う必要があるシェーダーの数を最小限に抑えることで、プロジェクトの規模を効率的にスケールさせることができるようになります。

A Clear Coat material feature enabled by the artist at authoring time, by enabling a shader_feature keyword
オーサリング時にアーティストが shader_feature キーワードを有効にすることで、クリアコートのマテリアル機能が有効になる。

条件付きシェーダー機能は、さまざまな方法で実装することができます。

  • 静的(コンパイル時)分岐
  • シェーダーバリアントのコンパイル
  • 動的(実行時)分岐

静的分岐によって、実行時に分岐に関連するシェーダー実行のオーバーヘッドを回避できますが、コンパイル時に評価・固定され、実行時の制御は行いません。一方、シェーダーバリアントのコンパイルは静的分岐の一種で、実行時の制御が追加されています。これは可能な限りの静的分岐の組み合わせに対して、固有のシェーダープログラム(バリアント)をコンパイルすることで機能する方法で、実行時に最適な GPU 性能を維持します。

このようなバリアントは、shader_feature および multi_compile のシェーダーキーワードによって、シェーダー機能を条件付きで宣言し、評価することによって作成されます。アクティブキーワードと実行時の設定に基づき、正しいシェーダーバリアントが実行時に読み込まれます。追加のシェーダーキーワードを宣言し評価することで、ビルド時間、ファイルサイズ、実行時のメモリ使用量が増加する可能性があります。

同時に、動的(ユニフォーム変数ベース)分岐は、シェーダーバリアントのコンパイルのオーバーヘッドを完全に回避し、ビルドの高速化、ファイルサイズとメモリ使用量の削減の両方を実現します。これにより、開発時のイテレーションをよりスムーズに、より速く行うことができます。

一方、動的分岐は、シェーダーの複雑さとターゲットデバイスによっては、シェーダーの実行性能に強い影響を与える可能性があります。分岐の一方がもう一方よりはるかに複雑になるような非対称な分岐はパフォーマンスに悪影響を及ぼす可能性があります。これは、より単純なパスを実行しても、より複雑なパスの性能上のペナルティが発生する可能性があるからです。

独自のシェーダーに条件付きシェーダー機能を導入する場合、これらのアプローチとトレードオフに留意する必要があります。より詳細な情報については、シェーダー条件分岐シェーダー分岐シェーダーバリアントのドキュメントをご覧ください。

シェーダーバリアントストリッピング

シェーダーの処理とコンパイルの時間の増加を緩やかにするために、シェーダーバリアントストリップを利用します。以下の要素をもとに、不要なシェーダーバリアントをコンパイルから除外することを目的としています。

  • 含まれるマテリアルと有効になっているキーワード
  • プロジェクトとレンダーパイプラインの設定
  • スクリプタブルストリッピング

シェーダーバリアントを列挙するとき、エディターは shader_feature で宣言されたキーワードのうち、参照されているマテリアルやビルドに含まれるマテリアルで有効になっていないものを自動的に除外します。その結果、これらのキーワードは追加のバリアントを生成することはありません。

たとえば、クリアコートのマテリアルプロパティが Complex Lit URP シェーダーを使用するどのマテリアルでも有効でない場合、クリアコート機能を実装するすべてのシェーダーバリアントはビルド時に安全にストリップされます。

一方、multi_compile キーワードは、開発者とプレイヤーが利用可能なプレイヤー設定とスクリプトに基づいて、実行時にシェーダーの機能を自由に制御できるように指示します。裏を返せば、このようなキーワードは shader_feature キーワードと同じ程度にエディターによって自動的にストリッピングされることはない、ということです。そのため、一般的にはより多くのバリアントを作成しています。

スクリプタブルストリッピングは C# API で、実行時には必要ないキーワードと組み合わせにより、ビルド時にシェーダーのバリアントをコンパイルから除外することができます。レンダーパイプラインは、ビルドに含まれるプロジェクトのレンダーパイプライン設定Quality Assets に従って、不要なバリアントのストリッピングを行うために、スクリプタブルストリッピングを利用します。

 低品質高品質バリアントの係数
メインライト/キャストシャドウ:オフオン2 倍
メインライト/キャストシャドウ:オンオン1 倍
メインライト/キャストシャドウオフオフ1 倍

エディターのシェーダーバリアントストリッピングの効果を最大にするために、グラフィックス関連の機能およびレンダーパイプラインの設定のうち、実行時に使用しないものをすべて無効にすることを推奨します。シェーダーバリアントストリッピングの詳細については、公式ドキュメントを参照してください。

シェーダーバリアントの事前フィルタリング

シェーダーバリアントストリッピングは、ビルドの Render Pipeline Quality Assets のような要素に基づいて、コンパイルされたシェーダーバリアントの量を大幅に削減します。しかし、現在はシェーダー処理ステージの最後にストリッピングが行われています。コンパイルするしないに関わらず、可能性のあるすべてのバリアントを列挙するだけでも、長い時間がかかることが依然としてあります。

シェーダーバリアント処理(およびプロジェクトビルド)の時間を短縮するため、エンジンに内蔵されたシェーダーバリアントストリッピングに大幅な最適化を導入しました。シェーダーバリアントの事前フィルタリングにより、クリーンビルドとウォームビルドの両方の時間を大幅に短縮しました。

レンダーパイプライン設定によって駆動される事前フィルタリング属性に従い、multi_compile キーワードの早期除外を導入することによって、この最適化が機能します。これにより、潜在的なストリッピングとコンパイルのために列挙されるバリアントの量が減り、その結果、シェーダーの処理時間が短縮されます。最も劇的な例では、ウォームビルド時間が 最大で 90% 短縮されています。

シェーダーバリアントの事前フィルタリングは Unity 2023.1.0a14 で初めて搭載され、バージョン 2022.2.0b152021.3.15f1 にバックポートされています。

Shader processing time reduction for warm project builds in URP Boat Attack
「URP Boat Attack」プロジェクトのウォームビルドにおけるシェーダー処理時間の短縮効果
Shader processing time reduction for warm project builds in URP Terrain Demo
「URP Terrain Demo」プロジェクトのウォームビルドにおけるシェーダー処理時間の短縮効果

バリアントの事前フィルタリングも同様の原理で、最初の(クリーン)ビルドの時間短縮に貢献します。

Shader processing time reduction for clean project builds in URP Terrain Demo
「URP Boat Attack」プロジェクトのクリーンビルドにおけるシェーダー処理時間の短縮効果
Shader processing time reduction for warm project builds in URP Terrain Demo
「URP Terrain Demo」プロジェクトのクリーンビルドにおけるシェーダー処理時間の短縮効果

動的なシェーダー読み込み

歴史的に、Unity のランタイムは、シーンとリソースの読み込み時に、すべてのシェーダーオブジェクトをディスクから CPU メモリにあらかじめ読み込んでいました。ほとんどの場合、ビルドされたプロジェクトとシーンには、アプリケーションの実行時の任意の瞬間に必要な数よりも多くのシェーダーバリアントが含まれています。大量のシェーダーを使用するプロジェクトでは、実行時にシェーダーメモリの使用量が多くなることがよくあります。

動的なシェーダー読み込みは、シェーダー読み込みの動作とメモリ使用量をユーザーが細かく制御できるようにすることで、この問題に対処しています。この最適化は、ユーザーが制御するメモリ予算に基づいて、シェーダーのデータチャンクのメモリへのストリーミングや、実行時に不要になったシェーダーデータの排除を可能にします。これにより、メモリ予算に制限のあるプラットフォームでシェーダーメモリの使用量を大幅に削減することができます。

新しいシェーダーバリアント読み込み設定は、エディターのプレイヤー設定からアクセスできるようになり ました。これらを使用して、読み込まれるシェーダーチャンクの最大数とシェーダーチャンクごとのサイズ(MB)をオーバーライドします。

Editor > Project Settings > Player > Shader Variant Loading Settings
Editor > Project Settings > Player > Shader Variant Loading Settings の順にたどる

以下の C# API が利用可能になったことで、エディタースクリプトを使用して、シェーダーバリアントの読み込み設定を上書きすることができます。

Shader.maximumChunksOverride を介して C# API を使用し、実行時に読み込まれるシェーダーチャンクの最大量をオーバーライドすることも可能です。これにより、実行時に照会される利用可能なシステムの合計およびグラフィックスメモリなどの要因に基づいて、シェーダーのメモリ予算をオーバーライドすることができます。

動的なシェーダー読み込みは Unity 2023.1.0a11 で初めて搭載され、バージョン 2022.2.0b102022.1.21f1 および 2021.3.12f にバックポートされています。ユニバーサルレンダーパイプライン(URP)の Boat Attack の場合、シェーダーの実行時のメモリ使用量が 315 MiB(デフォルト)から 66.8 MiB(動的読み込み)へと 78.8% 削減されていることが確認され ました。この最適化については、公式のアナウンスで詳しく説明されています。

Dynamic shader loading utilized in URP Boat Attack, leading to a 78.8% reduction in runtime shader memory usage.
URP Boat Attack で採用された動的なシェーダー読み込みにより、実行時のシェーダーメモリの使用量を 78.8% 削減した。

今後の予定

ここまでご紹介した重要な変更点以外にも、ユニバーサルレンダーパイプラインのシェーダーバリアント生成とストリッピングの強化に取り組んでいます。また、Unity のシェーダーバリアント管理についても、全般的にさらなる改善を検討しています。最終的な目標は、エンジンの機能拡張を容易にすると同時に、シェーダーのビルドと実行時のオーバーヘッドを最小限に抑えることです。

現在研究を進めている事柄として、類似のバリアント間のシェーダーリソースの重複排除や、シェーダーキーワードの全体的な改善、Shader Variant Collection API などがあります。シェーダーバリアントの処理と実行時の性能をより柔軟に制御できるようにすることが目的です。

今後の課題として、シェーダーバリアントのトレースと解析のためのエディター内ツールの可能性も探っています。シェーダーバリアントの使用状況について以下のような詳細を提供できるツールの提供を目指しています。

  • どのシェーダーとキーワードが最も多くのバリアントを生み出すのか
  • コンパイルされているが、実行時には使用されていないバリアントはどれか
  • ストリッピングの対象となったが、実行時にリクエストされたバリアントはどれか

これまで私たちは、皆さんからのご意見を、最も有意義なソリューションを優先的に提供する手助けとしてきました。公開ロードマップをご確認の上、皆さんのニーズに最も合った機能にご投票ください。もし追加で変更してほしいところがあれば、機能リクエストを出すか、こちらのシェーダーフォーラムで直接チームに連絡してください。

2022年12月28日 カテゴリ: Engine & platform | 13 分 で読めます

Is this article helpful for you?

Thank you for your feedback!

関連する投稿