Version: 2020.2
言語: 日本語
Optimizing shader variants
シェーダーを書く場合のパフォーマンスのヒント

非同期シェーダーコンパイル

非同期シェーダーコンパイルは、新しいシェーダーバリアントのコンパイル中にダミーのシェーダーをレンダリングすることによって Unity エディターがフリーズするのを防ぎます。非同期シェーダのコンパイルは、エディター設定 (Edit > Project Settings… > Editor > Shader Compilation) でデフォルトで有効になっています。

シェーダーは、数百または数千のバリアントで構成されており、さまざまなキーワードの組み合わせでさまざまな使用シナリオをカバーできます。シェーダーをロードするときにエディターがすべてのバリアントをコンパイルする必要がある場合、シェーダーのインポートプロセスは非常に遅くなります。 これを処理するために、Unity はデフォルトで “オンデマンドシェーダーコンパイル” を使用します。 これは、エディターがシーンで最初にシェーダーバリアントを検出したときにコンパイルすることを意味します。 シェーダーの完全なコンパイルにはミリ秒から数秒かかる可能性があるため、エディターでのレンダリングをフリーズさせる可能性があります。シェーダーバリアントのコンパイルにかかる時間は、選択したグラフィックス API とシェーダーの複雑さによって異なります。これらのフリーズの対策として、Asynchronous Shader Compilation (非同期シェーダーコンパイル) を使用します。

ゲームビューとシーンビューで非同期シェーダーコンパイルを無効にする

非同期シェーダーコンパイルはデフォルトで有効になっています。ゲームビューとシーンビューでは無効にすることができます。これは、レンダリングソリューションのどの部分が問題の原因となっているかわからない場合に便利です。ただし、それでもシェーダーのコンパイル中にエディターでレンダリングフリーズが発生する場合があります。

非同期シェーダーコンパイルを無効にするには、以下の手順を行います 。

  1. プロジェクトで、Edit > Project Settings.. > Editor の順に移動します。

  2. エディター設定の下部の Shader Compilation で、Asynchronous Shader Compilation のチェックを外します。

Asynchronous Shader Compilation のチェックボックスは、Project Settings > Editor > Shader Compilation 以下にあります。
Asynchronous Shader Compilation のチェックボックスは、Project Settings > Editor > Shader Compilation 以下にあります。

ノート: 非同期シェーダーコンパイルを無効にすると、シーンビューとゲームビューにのみ影響します。これは、非同期シェーダーコンパイルを明示的に有効にするシステムやカスタムスクリプトには影響しません。

非同期シェーダーコンパイルのしくみ

非同期シェーダーコンパイルでは、エディターはシェーダーバリアントをコンパイルキューに加え、ジョブスレッドで完了します。すぐにはコンパイルされません。レンダリングのフリーズを避けるために、エディターはシェーダーバリアントをコンパイルしている間もレンダリングを続行します。次に、エディターはシェーダーバリアントのスポットを単純なシアン色のダミーシェーダーで塗りつぶします (シアンのダミーシェーダーが一時的に見え、背景で何が起こっているかが分かります)。これにより、一部のオブジェクトがシーンでレンダリングされなくなることを防ぎます。なぜなら、エディターがまだコンパイルしているためです。コンパイルが終了すると、エディターは本物のシェーダーと入れ替えます。エディターの右下にあるプログレスバーにコンパイルキューの状態が表示されます。

エディターはビルドプロセス中にプレイヤーが必要とするすべてのシェーダーバリアントをコンパイルするため、この機能はスタンドアロンプレイヤーには影響しません。

ノート: 非同期シェーダーのコンパイルは、シーンビューとゲームビューでデフォルトです。他のシナリオで使用したい場合は、カスタムエディターツールでの非同期シェーダーコンパイルの使用 を参照してください。

Unity は、コンパイルが終了するまで、シアンのダミーシェーダーでコンパイルしているシェーダーバリアントをレンダリングします。右下のプログレスバーは、シェーダーコンパイルキューの進行状況を示します。
Unity は、コンパイルが終了するまで、シアンのダミーシェーダーでコンパイルしているシェーダーバリアントをレンダリングします。右下のプログレスバーは、シェーダーコンパイルキューの進行状況を示します。

ノート: DrawProceduralCommandBuffer.DrawProcedural を使用する場合、エディターはダミーのシェーダーをスワップしません。代わりに、エディターはシェーダーバリアントをコンパイルし終わるまで、このシェーダーバリアントのレンダリングをスキップします。

ノート: ビルド操作は非同期シェーダーコンパイルを使用しません。これは、たいていの一般的な使用時に正しい出力を保証するためです。

エディターの高度なレンダリング

デフォルトでは、非同期シェーダーコンパイルはシーンビューとゲームビューで機能します。高度なレンダリングソリューションは、一度データを生成し、それを後のフレームで再利用することに依存しています。これは、ダミーのシェーダーが生成されたデータを汚染する可能性があります。これが発生すると、シェーダーのコンパイルが終了した後でも、シーンにシアン色やその他のレンダリングのアーティファクトが出現します。この問題は、エディターがデータを生成するシェーダーバリアントに初めて遭遇したときにのみ発生します。これらの問題を回避するには、レンダリングの一部で非同期シェーダーコンパイルを無効にするか、一部のシェーダーで同期シェーダーコンパイルを強制するか、特定のデータ汚染を検出し、コンパイル後にデータを再生成します。

特定のレンダリングの呼び出しでの非同期シェーダーコンパイルの無効化

非同期シェーダーコンパイルを使用したいが、特定のレンダリングの呼び出しでレンダリング結果にダミーシェーダーによる影響を与えたくない場合は、C# スクリプトでそれらの呼び出しの機能を無効にすることができます。以下の手順は、immediate (直近) スコープと CommandBuffer スコープで機能を無効にする方法を示しています。どちらの場合も基本的に、Unity が非同期にコンパイルしたくないレンダーコールの周りにクランプを挿入します。

immediate スコープ

immediate のスコープでは、ShaderUtil.allowAsyncCompilation; を使用します。

ShaderUtil.allowAsyncCompilation の現在の状態を変数に保存します。 非同期コンパイルを無効にするレンダーコマンドを呼び出す直前に、ShaderUtil.allowAsyncCompilationfalse に設定します。 これを適用したいレンダリングの呼び出しが終わったら、ShaderUtil.allowAsyncCompilation を最初に保存した以前の状態に戻します。

以下は擬似コードの例です。

// 現在の状態を保存し、非同期コンパイルを無効にします
bool oldState = ShaderUtil.allowAsyncCompilation;
ShaderUtil.allowAsyncCompilation = false;

// ダミーシェーダーを使用してはならないレンダリングコードを入力します
Graphics.DrawMesh(...);

//以前の状態に戻します
ShaderUtil.allowAsyncCompilation = oldState;

CommandBuffer スコープ

CommandBuffer スコープでは、ShaderUtil.SetAsyncCompilationShaderUtil.RestoreAsyncCompilation を使用します。

非同期コンパイルを無効にするレンダーコマンドを呼び出す直前に、ShaderUtil.SetAsyncCompilation を呼び出し false に設定します。バッファ内のそれ以降のコマンドはすべて、非同期コンパイルを許可しません。 これを適用するレンダリングコマンドの後に、Shader.Util.RestoreAsyncCompilation を使用します。

以下は擬似コードの例です。


//これ以降のコマンドの非同期コンパイルを無効にします
ShaderUtil.SetAsyncCompilation(cmd, false);

/// ダミーシェーダーを使用してはならないレンダリングコマンドを入力します
cmd.DrawMesh(...);

// 以前の状態に戻します
ShaderUtil.RestoreAsyncCompilation(cmd);

特定のシェーダーに対する同期コンパイルの強制

特定のシェーダーに対して同期コンパイルを強制できます。これは、レンダリングの開始時に常に存在し、比較的高速でコンパイルするデータ生成シェーダーに適したオプションです。

特定のシェーダーに同期コンパイルを強制するには、シェーダーコードに次の ディレクティブ を追加します: #pragma editor_sync_compilation

ノート: レンダリングの途中で新しいバリアントに遭遇する複雑なシェーダーに対して、同期コンパイルを強制しないでください。複雑なシェーダーを強制的に同期コンパイルすると、エディターでレンダリングが停止する場合があります。

コンパイルの検出とデータの更新

ダミーシェーダーが 生成したデータを汚染する 場合は、汚染されたデータを破棄し、適切にコンパイルされたシェーダーで新しいセットを再生成する必要があります。

Unity がデータ生成に使用しているマテリアルが認識される場合は、ShaderUtil.IsPassCompiled を使用して、マテリアルの現在の状態で指定されたシェーダーバリアントのコンパイルステータスを確認します。 ステータスが Uncompiled (未コンパイル) から Compiled (コンパイル済み) になったら、生成されたデータを更新します。

汚染されたデータを生成するマテリアルが特定できない場合や、データ生成が 1 つのマテリアルに限定されない場合は、以下を行います。

ShaderUtil.anythingCompiling を使用して、Unity がシェーダーを非同期でコンパイルしているかどうかを検出します。 非同期コンパイルが終了したら、データを更新します。

カスタムエディターツールでの非同期シェーダーコンパイルの使用

デフォルトでは、 非同期シェーダーコンパイルはゲームビューとシーンビューで動作します。カスタム製のエディターツールで使用したい場合は、カスタムツールの C# を使って有効にできます。

immediate スコープ

immediate のスコープでは、ShaderUtil.allowAsyncCompilation; を使用します。

ShaderUtil.allowAsyncCompilation の現在の状態を変数に保存します。 非同期コンパイルを有効にするレンダーコマンドを呼び出す直前に ShaderUtil.allowAsyncCompilationtrue に設定します。 これを適用するレンダリングの呼び出しが終わったら、ShaderUtil.allowAsyncCompilation を最初に保存した時点の状態に戻します。

以下は擬似コードの例です。

//現在の状態を保存し、非同期コンパイルを有効にします
bool oldState = ShaderUtil.allowAsyncCompilation;
ShaderUtil.allowAsyncCompilation = true;

// 非同期コンパイルを使用したいレンダリングコードを入力します
Graphics.DrawMesh(...);

// 以前の状態に戻します
ShaderUtil.allowAsyncCompilation = oldState;

CommandBuffer スコープ

CommandBuffer ベースのスコープでは、ShaderUtil.SetAsyncCompilationShaderUtil.RestoreAsyncCompilation を使う必要があります。

非同期コンパイルを有効にするレンダーコマンドを呼び出す直前に ShaderUtil.SetAsyncCompilation を呼び出し、true に設定します。バッファ内のそれ以降のコマンドはすべて、非同期コンパイルを許可します。 これを適用したいレンダリングコマンドの後に、Shader.Util.RestoreAsyncCompilation を使用します。

以下は擬似コードの例です。


// これ以降のコマンドの非同期コンパイルを有効にします
ShaderUtil.SetAsyncCompilation(cmd, true);

/// 非同期コンパイルを使用するレンダリングコマンドを入力します
cmd.DrawMesh(...);

// 以前の状態に戻します
ShaderUtil.RestoreAsyncCompilation(cmd);

Graphics.ExecuteCommandBuffer(cmd);

コンパイル時のレンダリングのカスタマイズ

カスタムツールでマテリアルごとのダミーシェーダー以外のものを描画することができます。このようにして、シアンになる可能性のあるシェーダーバリアントでレンダリングすることを避け、代わりにシェーダーのコンパイル中に何か他のものを描画することができます。

特定のシェーダーバリアントがコンパイルされたかどうかを確認するには、ShaderUtil.IsPassCompiled を使用します。

手動でコンパイルをトリガーするには、ShaderUtil.CompilePass を使用します。このようにして、シアンになる可能性のあるシェーダーバリアントでレンダリングすることを避け、代わりにシェーダーのコンパイル中に何か他のものを描画することができます。


  • 2019–03–07 公開ページ
  • 非同期シェーダーコンパイルは 2019.1 で追加NewIn20191
Optimizing shader variants
シェーダーを書く場合のパフォーマンスのヒント