Version: 2023.1
言語: 日本語
シェーダーにおける分岐
シェーダーバリアントの数の確認

シェーダーバリアント

シェーダーバリアントはシェーダー順列とも呼ばれ、シェーダーコードに条件付き動作を導入する方法の 1 つです。

Unity は、シェーダーのソースファイルをシェーダープログラムにコンパイルします。コンパイルされた各シェーダープログラムは、1 つまたは複数の バリアント を持っています。これにより、さまざまな条件に応じて、さまざまなバージョンのシェーダープログラムを使用できます。ランタイム に、Unity は現在の要件に一致するバリアントを使用します。シェーダーキーワード を使用してバリアントを設定します。

シェーダーコードにおける条件に関する概要と、どちらの手法をどのような場合に使用するかについては、シェーダーコードにおける条件分岐 を参照してください。Unity がシェーダーバリアントをロードする方法の詳細は、シェーダーのロード を参照してください。

多くのバリアントを持つシェーダーは、“メガシェーダー” または “ウーバーシェーダー” と呼ばれます。Unity のスタンダードシェーダーは、その一例です。

シェーダーバリアントの長所と短所

シェーダーバリアントの主な利点は、動的分岐 の GPU パフォーマンスへの影響なしに、シェーダープログラムでランタイム条件を使用できることです。シェーダーバリアントの主な欠点は、バリアントの数が多いと、ビルド時 とランタイムの両方のパフォーマンスの問題の原因になる可能性があることです。

Unity がシェーダーバリアントを作成するとき。静的分岐 を使って、小さな特化型シェーダープログラムを複数作成します。ランタイムに、Unity は条件に一致するシェーダープログラムを使用します。つまり、GPU パフォーマンスのペナルティを受けることなく、動的ブランチで GPU パフォーマンスの低下を引き起こす可能性のあるコードにシェーダーバリアントを使用できます。

ただし、多数のバリアントがあると、ビルド時間、ファイルサイズ、ランタイムメモリ使用量、ロード時間が増加します。また、手動でシェーダーをプリロード (事前準備) する場合の複雑さが増すことにもつながります。プロジェクトに非常に多くのシェーダーバリアントが含まれる場合、これらの問題はパフォーマンスとワークフローに重大な問題をもたらす可能性があります。

注意: 不注意に過剰な数のシェーダーバリアントを作成することは簡単で、これは重大なパ フォーマンス問題につながる可能性があります。したがって、Unity がシェーダーバリアントの数を決定する方法コンパイルから不要なバリアントを除外 (ストリップ) する方法シェーダーでいつ他のタイプの条件を使用するか を理解することが非常に重要です。

シェーダーバリアントの数

ビルド時に、Unity は現在のビルドターゲットの各グラフィックス API に対して 1 セットのシェーダーバリアントをコンパイルします。各グラフィックス API とビルドターゲットの組み合わせに対するバリアントの数は、シェーダーのソースファイルとシェーダーキーワードの使用に依存します。

シェーダーバリアントの数を確認 できます。

グラフィックス API

Unity は現在のビルドターゲットの各グラフィックス API に対して 1 セットのシェーダーバリアントをコンパイルします。シェーダーは、各ビルドターゲットとグラフィックス API の組み合わせごとに異なります。例えば、Unity は iOS の Metal と macOS の Metal に対して、異なるシェーダーをコンパイルします。

シェーダープログラムやキーワードによっては、指定のグラフィックス API やビルドターゲットのみを対象とするものもあります。そのため、グラフィックス API とビルドターゲットの組み合わせごとのバリアントの数は異なります。ただし、これらのバリアントをコンパイルする手順は同じです。

現在のビルドターゲットのグラフィックス API のリストを表示および編集するには、Player 設定 ウィンドウ、または PlayerSettings API を使用します。

シェーダープログラムの数

Unity は、現在のビルドターゲットとグラフィックス API の組み合わせに対して、コンパイルするシェーダープログラム数を決定する必要があります。

ビルドに含まれるシェーダーのソースファイルごとに、固有のシェーダープログラムをいくつ定義するかが決定されます。

  • コンピュートシェーダーアセットは、1 つのシェーダープログラムを定義します。
  • ハンドコードされたシェーダーでは、シェーダープログラムの数はコードに依存します。総数は以下から構成されます。
    • ソースファイルのすべてのパスのすべてのシェーダーステージ。例えば、各頂点ステージで 1 つのシェーダープログラムを定義し、各フラグメントステージで 1 つのシェーダープログラムを定義する、というように。
    • ソースファイルの依存関係にあるすべてのパスのすべてのシェーダーステージ。これには、すべての フォールバックシェーダーUsePass コマンド を使用するすべてのパスが含まれます。
  • Shader Graph シェーダーでは、シェーダープログラムの数は、Unity がグラフから生成するコードに依存します。Unity が生成するシェーダーコードを見るには、Shader Graph アセットを右クリックし、 See generated code を選択します。その後、ハンドコードされたシェーダーの場合と同じ方法で、シェーダープログラムの総数が決まります。

ノート: シェーダーのソースファイルは、ビルドのシーンで参照されている場合、Resources フォルダーの何かによって参照されている場合、Graphics Settings ウィンドウの Always-included Shaders セクションに含まれている場合に、そのビルドに含まれます。

シェーダープログラムに影響を与えるキーワード

Unity は、現在のビルドターゲットとグラフィックス API に対して、コンパイルするシェーダープログラム数を決定し、次に、各シェーダープログラムに対してコンパイルしなければならないシェーダーバリアントの数を決定します。

各シェーダープログラムに対して、Unity はそのプログラムに影響を与えるシェーダーキーワードの組み合わせを決定します。これは以下によって構成されています。

Unity が 1 つのシェーダープログラムに対してコンパイルするシェーダーバリアントの数は、キーワードの積です。つまり、Unity は各セットから 1 つの要素を含むすべてのセットに対して 1 つのバリアントをコンパイルします。

例えば、このセットには 3 つのシェーダーバリアントキーワードが含まれています。

  • COLOR_RED
  • COLOR_GREEN
  • COLOR_BLUE

このセットには 4 つのシェーダーバリアントキーワードが含まれています。

  • QUALITY_LOW
  • QUALITY_MEDIUM
  • QUALITY_HIGH
  • QUALITY_ULTRA

これらのシェーダーバリアントキーワードの影響を受けるシェーダープログラムは、以下の 12 バリアントになります。

  • COLOR_RED、QUALITY_LOW
  • COLOR_RED、QUALITY_MEDIUM
  • COLOR_RED、QUALITY_HIGH
  • COLOR_RED、QUALITY_ULTRA
  • COLOR_GREEN、QUALITY_LOW
  • COLOR_GREEN、QUALITY_MEDIUM
  • COLOR_GREEN、QUALITY_HIGH
  • COLOR_GREEN、QUALITY_ULTRA
  • COLOR_BLUE and QUALITY_LOW
  • COLOR_BLUE and QUALITY_MEDIUM
  • COLOR_BLUE and QUALITY_HIGH
  • COLOR_BLUE and QUALITY_ULTRA

シェーダーバリアントキーワードのセットを追加すると、Unity がコンパイルするバリアントの数は非常に急速に増加する可能性があります。この非常に急速な増加を表す用語は “組合せ爆発” (combinatorial explosion) です。

例えば、かなり典型的なユースケースを考えてみましょう。シェーダーが、それぞれ 2 つのキーワード (<feature name>_ON<feature name>_ OFF) を持つ多くのキーワードセットを持っている場合です 。シェーダーがそのキーワードセットを 2 個持っている場合、これは 4 つのバリアントになります。シェーダーがそのキーワードセットを 10 個持っている場合は、1024 つのバリアントになります。

シェーダーバリアントの重複排除

コンパイル後、Unity は同一パス内の同一バリアントを自動的に識別し、これらの同一バリアントが同じバイトコードを指すようにします。これを 重複排除 と呼びます。

重複排除は、同一パス内の同一バリアントによるファイルサイズの増加を防ぎますが、同一バリアントがあると、コンパイル時に無駄な作業が発生したり、実行時にメモリ使用量やシェーダーのロード時間が増加したりします。この点を考慮して、常に、必要のないバリアントのストリッピング を行うことが大切です。

シェーダーにおける分岐
シェーダーバリアントの数の確認