【Unity】shader_feature_localやmulti_compile_localを正しく使おうというお話

Unityのshader_feature_localやmulti_compile_localを正しく使おうというお話です。

Unity2020.3.15f2

グローバルキーワード・ローカルキーワード

Unityのシェーダでは、shader_featureやmulti_compileでディレクティブを記述することでバリアントを作ることができます。

light11.hatenadiary.com

これは便利な機能ですが、これらで定義できるキーワード(=グローバルキーワード)の合計数は1プロジェクト内で256個や384個(バージョンによる)といった制限があります(Unity2021.2までは / 後述)。
さらにUnityのビルトインシェーダがすでに使っているキーワードも多数あるので、実際はさらに少なくなります。

この制限への対策として、Unity2019からshader_feature_localとmulti_compile_localというディレクティブが登場しました。
これらを使うとシェーダキーワードを「ローカルキーワード」として定義できます。

ローカルキーワードは一つのシェーダに64個まで定義可能で、グローバルキーワードのように1プロジェクトにおける制限は存在しません。
特有の制限として、Shader.EnableKeyword()などのグローバルキーワードを操作するAPIではローカルキーワードを操作することができません。
使えるAPIと使えないAPIは以下の通りです。

API 操作対象のキーワード
Shader.EnableKeyword()
Shader.DisableKeyword()
グローバルキーワードのみ
※ローカルキーワードは操作できない
CommandBuffer.EnableShaderKeyword()
CommandBuffer.DisableShaderKeyword()
グローバルキーワードのみ
※ローカルキーワードは操作できない
Material.EnableKeyword()
Material.DisableKeyword()
グローバルキーワード
ローカルキーワード
ComputeShader.EnableKeyword()
ComputeShader.DisableKeyword()
グローバルキーワード
ローカルキーワード

また、できる限りローカルキーワードを使うことでパフォーマンスの向上にもつながるようです。

You might see a change in performance when you start using local keywords, but the difference depends on how your Project is set up. The total number of local and global keywords per shader affects performance: in an ideal set-up, use more local keywords and fewer global keywords, to reduce the total keyword count per shader.
Unity - Manual: Declaring and using shader keywords in HLSL より

これらの仕様を踏まえて、基本的にグローバルな操作を行わないもの以外はローカルキーワードを使うべきです。

注意点

ローカルキーワードを使う上で、一点注意が必要そうな点がマニュアルに記載されていました。

If a Material has a local keyword enabled, and its shader changes to one that is no longer declared, Unity creates a new global keyword. Shader variants and keywords - Unity マニュアル より

つまり

  1. あるローカルキーワードをマテリアルに対して有効化する
  2. そのマテリアルのシェーダを変更する
  3. 2.のシェーダに1.のローカルキーワードが定義されていないと、それはグローバルキーワードとして扱われる

ということです。

普通に使っている分には問題にならなそうですが、仕様として覚えておいた方が良さそうです。

各シェーダステージ用suffixにも対応

シェーダバリアントが特定のシェーダステージでしか使われない場合にはディレクティブにfragmentやvertexといった接尾辞をつけることができます。
この機能はローカルディレクティブにも有効なので、以下のようにステージを指定したディレクティブを記述できます。

  • shader_feature_local_vertex
  • shader_feature_local_fragment
  • multi_compile_local_vertex
  • multi_compile_local_fragment

Unity2021.2からはシェーダキーワード上限数の制限が大幅に緩和

さてここまで現状のUnityの話をしてきましたが、現在ベータ版のUnity2021.2からはシェーダキーワードの上限数が大幅に増える予定です。

  • グローバルキーワード: 4,294,967,294 個まで
  • ローカルキーワード: 65,534個まで

実質無制限と言って良さそうです。

というわけでUnity2021.2以降を対象とする場合にはグローバルキーワードの個数問題を気にする必要はなさそうですが、
前述のパフォーマンスの話やコンパイル時間のことを考えると適切なディレクティブを記述するべきという方針は変わらないと思われます。

参考

docs.unity3d.com