【Unity】ビルド時に不要なシェーダバリアントを削除するIPreprocessShadersの使い方

Unityでビルド時に不要なシェーダバリアントを削除するIPreprocessShadersの使い方をまとめました。

Unity2020.3.15f2

ビルド時にシェーダバリアントを削除?

multi_compileやshader_featureが書かれたシェーダはバリアントが作られます。

light11.hatenadiary.com

まずshader_featureは、実際にプロジェクトで使用しているキーワードだけが有効とみなされます。
これに対してmulti_compileで独自に定義したキーワードに関しては、そのすべてのキーワードが有効となります。
そして有効なキーワードの組み合わせ数分だけシェーダバリアントが作成されます。

有効となっているキーワードやバリアントの数はシェーダのインスペクタからみることができます。

シェーダのインスペクタ

IPreprocessShadersは、ここからさらにビルドをする際に、シェーダバリアントを削ることができる機能です。
本記事ではこの機能の使い方についてまとめます。

ちなみに、上に「multi_compileで独自に定義したキーワードに関しては」と書きましたが、
Unityが定義しているものに関しては、プロジェクト設定などに応じて有効なキーワードが適宜切り替わります。
したがって設定をきちんとしていないとキーワードの組み合わせ数が爆発してパフォーマンスに影響を及ぼします。

IPreprocessShadersの使い方

さてそれではIPreprocessShadersを使ってバリアントを削る処理を書いてみます。
以下では「_TEST」というキーワードを持つバリアントをビルド時に削ります。

using System.Collections.Generic;
using UnityEditor.Build;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;


public class ExampleShaderPreprocessor : IPreprocessShaders
{
    private readonly ShaderKeyword _testKeyword;

    public ExampleShaderPreprocessor()
    {
        _testKeyword = new ShaderKeyword("_TEST");
    }

    // 値が小さいほど先に処理される
    // URPのIPreprocessShadersのOrderは0
    public int callbackOrder => 1;

    public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
    {
        // バリアント毎のループ
        for (var i = data.Count - 1; i >= 0; --i)
        {
            if (!data[i].shaderKeywordSet.IsEnabled(_testKeyword))
            {
                continue;
            }
            
            // 対象のキーワードが有効になっていたらそのバリアントを削除する
            data.RemoveAt(i);
        }
    }
}

説明はコメントに書いた通りです。

処理されるタイミング & 注意点

このIPreprocessShadersに書いた処理が行われるタイミングは、プレイヤーやアセットバンドルのビルド時になります。
したがってこの機能を使った場合、最終的なバリアント数はシェーダのインスペクタから見れるバリアントの数よりも少なくなります。

またURPではこの機能を使ってビルド時に不要なバリアントを削っています。
したがってURPにおける正確なキーワードやバリアントの数はビルドしてみないとわからないと言えます。

参考

blog.unity.com