SRP Batcher良き良き

SRPを導入するならSRP Batcher使いたい!

SRP Batcherの解説といえば公式Blog。充実した内容です。

blogs.unity3d.com

Unity 2019.2からOpen GL ES 3.1もサポートし、幅広く使えるようになりました。

SRP Batcherは、Materialが切り替わってもShaderVariantさえ変わらなければ高効率な描画を維持できるという点で従来描画より優れています。

Dynamic BatchはMaterialに違いがあればバッチが効かず、また効いたとしてもMesh結合がCPUに優しくないため頂点数の多い3Dプロジェクトではデフォルトでオフになりました。

GPU Instancingは高効率ですが、メッシュが同一の描画のみを束ねます。メッシュが異なるとbreak。

SRP Batcherはメッシュ結合を行わないためDrawCallは減りませんがMaterialが切り替わってもOKということで使えるシーンが広がります。

https://blogs.unity3d.com/wp-content/uploads/2019/02/image3-5.png 公式blogより画像引用

ShaderはSRP Batcher対応のものを使わなければいけませんが、対応は拍子抜けするぐらい簡単です。

比較のために最初にSRP Batcher非対応のシェーダを書いて、次にSRP Batcherに対応させてみます。

というわけで以下はTextureに色を乗算する簡単なTransparentシェーダ 。この時点ではSRP Batcherとはcompatibleではありません。

Shader "SRPB/Hoge"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags
        {
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
            "PreviewType" = "Plane"
            "CanUseSpriteAtlas" = "True"
        }

        Cull Off
        ZWrite Off
        ZTest Always
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
    
            fixed4 _Color;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv) * _Color;
                return col;
            }
            ENDCG
        }
    }
}

f:id:enrike3:20191011001112p:plain

_Colorが白と赤のMaterialを用意して、QuadとSphereで描画してみます。

f:id:enrike3:20191011003617p:plain Sphereはちょっとグロかった、反省している…
Materialが分かれているので、同一のShaderVariantでもSetPassCallが分かれてBatches(DrawCall)と同数です。

f:id:enrike3:20191011001512p:plain
FrameDebugger。Materialが異なるので当たり前ですがQuadとSphereの描画は別

シェーダをSRP Batcherとcompatibleに修正する

fixed4 _Color;

CBUFFER_START(UnityPerMaterial)
    fixed4 _Color;
CBUFFER_END

に書き換えるだけです。

f:id:enrike3:20191011002126p:plain
無事compatibleになりました

f:id:enrike3:20191011002402p:plain
DrawCallであるところのBatchesは据え置きですが、SetPassCallは一つ減ります。

f:id:enrike3:20191011002713p:plain
FrameDebugger。SRP BatcherによってSetPassCall1に対してDrawCallが2になっていることが表示されます。 Dynamic Batchだと1Meshの描画になるのでDrawCall自体が減るのですが、SRP BatcherはMesh結合をしないのでDrawCallは減りません。

このあたりはRenderDocでみると、もっとはっきりとわかります。

f:id:enrike3:20191011003221p:plain
QuadとSphereのDrawCallはちゃんと別々ですね。

まとめ

SRP Batcher良き。 シェーダの対応も簡単なので今後シェーダを書くときは最初からSRP Batcher対応シェーダを書くように癖をつけていこうと思ってます。