Version: 2021.1
シェーダーアセット
非同期シェーダーコンパイル

シェーダーのコンパイル

概要

プロジェクトをビルドするたびに、Unity エディターはビルドに必要なすべてのシェーダーをコンパイルします。必要なグラフィックス API ごとに、必要なシェーダーバリアントをすべてコンパイルします。

Unity エディターでは、すべてを先行してコンパイルすることはありません。すべてのグラフィックス API に対応するすべてのバリアントをコンパイルするには、非常に長い時間がかかるからです。

その代わりに、Unity エディターは以下のようにしています。

  • シェーダーアセットをインポートする場合、一部の最小限の処理 (サーフェスシェーダーの生成など) を行います。
  • シェーダーバリアントを表示する必要がある場合、Library/ShaderCache フォルダーを確認します。
  • 以前にコンパイルされたシェーダーバリアントが同一のソースコードを使用していることを見つけた場合、それを使用します。
  • 一致するものが見つからなかった場合。必要なシェーダーバリアントをコンパイルして、それをキャッシュに保存します。

シェーダーのコンパイルは、UnityShaderCompiler というプロセスを使って行われます。複数の UnityShaderCompiler プロセスを開始することができます (通常、マシンの CPU コアごとに 1 つ)。そのため、プレイヤーのビルド時にシェーダーのコンパイルを並行して行うことができます。エディターがシェーダーをコンパイルしていない間はコンパイラー処理は行われず、コンピューターのリソースを消費しません。

頻繁に変更されるシェーダーが沢山ある場合は、シェーダーキャッシュフォルダーが非常に大きくなる可能性もあります。このフォルダーの削除は安全で、Unity がシェーダーバリアントを再コンパイルするだけです。

プレイヤーのビルド時には、すべての “まだコンパイルされていない” シェーダーバリアントがコンパイルされるため、たまたまエディターがそれらを使用しなかった場合でもゲームデータに含まれます。

さまざまなシェーダーコンパイラー

異なるプラットフォームは、シェーダープログラムのコンパイルに異なるシェーダーコンパイラーを使用します。以下はその例です。

  • DirectX を使用するプラットフォームは、Microsoft の FXC HLSL コンパイラーを使用します。
  • OpenGL (Core & ES) を使用するプラットフォームは、Microsoft の FXC HLSL コンパイラーを使用し、その後、HLSLcc を使用して、バイトコードを GLSL への変換を行います。
  • Metal を使用するプラットフォームは、Microsoft の FXC HLSL コンパイラーを使用し、その後、HLSLcc を使用して、バイトコードを Metal に変換しています。
  • Vulkan を使用するプラットフォームは、Microsoft の FXC HLSL コンパイラーを使用し、その後、HLSLcc を使用して、バイトコードを SPIR-V に変換します。
  • コンソールプラットフォームなどその他のプラットフォームでは、それぞれのコンパイラーを使用します。
  • サーフェスシェーダーステップ は、コード生成解析ステップのために HLSL と MojoShader を使用します。

pragma ディレクティブ を使用して、さまざまなシェーダーコンパイラーの設定を行うことができます。

キャッシングシェーダープリプロセッサー

シェーダのコンパイルにはいくつかのステップがあります。最初のステップの 1 つは前処理です。このステップでは、プリプロセッサー と呼ばれるプログラムが、コンパイラーのシェーダーソースコードを準備します。

以前のバージョンの Unity では、エディターは現在のプラットフォームのシェーダーコンパイラーが提供するプリプロセッサーを使用していました。現在では、Unity の Caching Shader Preprocessor (キャッシングシェーダープリプロセッサー) を使用するか、以前の動作に戻すかを選択することができます。問題がない限り、キャッシングシェーダープリプロセッサーを使用してください。

キャッシングシェーダープリプロセッサーは、シェーダーのインポートとコンパイルを高速化するために最適化されており、最大で 25% の高速化できます。これは、中間的な前処理データをキャッシュすることで機能します。そのため、エディターがそのファイルを解析する必要があるのは、インクルードファイルのコンテンツが変更されたときだけです。これにより、同じシェーダーの複数のバリアントをより効率的にコンパイルすることができます。キャッシングシェーダープリプロセッサーを有効にする際に最も著しい効果を発揮するのは、プロジェクト内のシェーダーが共通のインクルードファイルを大量に使用している場合です。

キャッシングシェーダープリプロセッサーは、パフォーマンスの向上のみでなく、以下のような利点も加えます。

  • #pragma ディレクティブの条件内での限定的なサポート。
  • #pragma warning ディレクティブのサポート。
  • #include_with_pragmas ディレクティブのサポート。これにより、インクルードファイルに #pragma ディレクティブを加えることができます。
  • シェーダーインスペクターPreprocess Only チェックボックスによって、そのシェーダーアセットの事前処理されたソースを見ることができます。

キャッシングシェーダープリプロセッサーと従来の動作の違いについての詳細は、Unity フォーラムの New shader preprocessor を参照してください。

キャッシングシェーダープリプロセッサーは、Editor settings ウィンドウの Shader Compilation セクションの Caching Shader Preprocessor チェックボックス、または EditorSettings.cachingShaderPreprocessor API で有効または無効にすることができます。

ビルド時のストリッピング

ゲームのビルド中に、Unity は内部シェーダーバリアントの一部がゲームで使用されていないことを検出し、ビルドデータから除外 (ストリップ) することができます。ビルド時のストリッピングは以下のように行われます。

  • #pragma shader_feature を使用するシェーダーでは、Unity はバリアントが使用されているかどうかを自動的にチェックします。ビルド内のマテリアルのどれもがバリアントを使用していない場合、そのバリアントはビルドに含まれません。内部シェーダーバリアント ドキュメントを参照してください。スタンダードシェーダー はこれを使用します。
  • いずれのシーンでも使用されていないフォグとライトマップモードを処理するシェーダーバリアントは、ゲームデータに含まれません。この動作をオーバーライドしたい場合は、Graphics ウィンドウを参照してください。
  • また、手動でバリアントを識別し、OnProcessShader API を使用して、ビルドから除外するよう Unity に指示することもできます。

上記の組み合せは、シェーダーデータサイズをしばしば大幅に減らします。例えば、完全にコンパイルした Standard シェーダーは数百メガバイトを取るでしょう。しかし、典型的なプロジェクトでは、多くの場合、わずか数メガバイトを取ることになります(しばしばアプリケーションのパッケージングプロセスによってさらに圧縮されます)。

シェーダーアセット
非同期シェーダーコンパイル