Tips

【Unity】バッチングでドローコールを減らす

バッチング処理とは

画面を描画する際に、描画グラフィックAPIを呼び出す必要があり、ドローコールと呼ばれてます。ドローコールは高コストで呼ばれる回数が多くなるとCPUのパフォーマンスに影響が出ます。
そのため、ドローコールを少なくすることが大切になってきます。

Unityではバッチングと呼ばれる処理でドローコールを減らしています。バッチングは、描画対象のうちでまとめられるものはまとめて一回のドローコールで描画できるようにする処理です。パフォーマンスの観点からはバッチング処理をなるべく利用していくこと大切になってきます。

バッチングの種類

Unityでは2種類のバッチングがあります。ひとつはダイナミックバッチング、もうひとつはスタティックバッチングです。

ダイナミックバッチング

条件を満たしている場合にUnityで自動的に行われるバッチング処理です。条件を満たす場合にのみバッチング処理されるため条件をある程度知っておく必要があります。条件には、同じマテリアルを利用する事やメッシュの頂点数などがあります。詳しくは後述します。

スタティックバッチング

Inspector右上のStatic flagにチェックを入れる場合に行われるバッチング処理です。動かないオブジェクトに対して設定してください。ダイナミックバッチングより効果的にバッチング処理を行うことができますが、形状の情報はメモリに保存するためメモリとのトレードオフになります。

ダイナミックバッチングを利用する

スタティックバッチングが動かないオブジェクトという制約の関係上使えるタイミングが限定的なため、基本はダイナミックバッチングを利用していくことでドローコールを減らすことになります。しかしながら、条件が複雑です。

  • 同じマテリアルのインスタンスを利用している
  • トータル頂点数が900以下
  • シェーダーが頂点位置や法線や一つのUV情報を使っている場合 トータル頂点数 300以下
  • 頂点位置,法線,UV0,UV1,タンジェントを使っている場合 トータル頂点数 180以下
  • 同じスケール
  • ライトマップを持つ場合 バッチング対象外
  • マルチパスシェーダの場合 バッチング対象外
  • リアルタイムシャドウを投影されるオブジェクト の場合バッチング対象外

上のように列挙しましたが、頂点数などは変更されるかもしれないと公式ドキュメントに書いてあったり、他にも細かい条件はあるようです。細かいところまで把握するのは難しいため、とりあえず同じマテリアルインスタンス を共有しているオブジェクトがバッチング対象になりうる事を覚えておきたいです。

同じマテリアルを利用する

バッチングの前提条件として、同じマテリアルインスタンスを共有していることが必要です。注意すべきことは同じマテリアルではなく同じマテリアルインスタンスである必要がある事です。例えば、MeshRendererには、 materialプロパティと sharedMaterialプロパティ があります。コード上でマテリアルを割り当てる際に、materialプロパティにマテリアルを代入した場合、マテリアルはコピーされ別インスタンスとなるためオブジェクトはバッチング対象になりません。一方でsharedMaterialプロパティに代入した場合、同じインスタンスを共有できバッチング対象となります。

テクスチャのアトラス化でバッチングに対応する

参照するテクスチャのみ異なるマテリアルがある場合は、テクスチャをアトラス化して1つのマテリアルにすることでバッチング対象にできます。また、Spriteの場合、同じテクスチャであればバッチング対象になります。そのため、可能であれば同時に描画したいテクスチャをアトラス化しておく事が望ましいです。

FrameDebuggerでバッチング処理を追う

FrameDebuggerはドローコールを順に確認できるUnityのツールです。バッチングの状況及びバッチング対象外の理由などが分かるため、バッチングの最適化で役立ちます。ツールは、Unityメニュ上のWindow/Analysis/FrameDebuggerから開く事ができます。ツールを開いたらEnableを押しましょう。

画面上では、別のテクスチャーからなる3つのトランプカードを配置しています。別々のテクスチャを利用しているためバッチング処理は走りません。そのためDrawDynamicが3回走っています。

2回目のDrawDynamicを見てみると 「Why this draw call can’t be batched ..」と前のバッチングにまとめることができなかった理由が書かれています。異なるマテリアルを持っていることが原因のようです。描画オブジェクトはSpriteなのでテクスチャ起因ですね。ここで、テクスチャアトラス化したトランプで表示してみます。

3回呼ばれていたDrawDynamic が1回になる事を確認できました。

Pocket