Unityでちょっとしたテストをしていたのですが、 なぜかDraw Callの値が非常に高くなっていたので調べてみたところ球の描画に問題があったようです。
UnityのSphereメッシュ(球)は動的バッチングをすることができず、 追加するとその分だけDraw Callの数が増えるということらしいです。
個人的に衝撃だったので記事にしてみました。
前提知識
Draw Call(ドローコール)とは?
Draw CallとはGPUに対する描画命令呼び出しのことで、一般的に重たい処理とされています (最新のアーキテクチャではこの常識も崩れつつあるとかないとか)。
重たい理由はGPUが描画に使うための情報をCPUで準備していることにあるそうです (そのためDraw Callが増えるとGPUではなくCPUが重くなります)。 この手の話は「Draw Call」で検索すると日本語でも多くの資料がヒットするはずなのでここでは省略します。
Draw Callの数はUnityエディタのゲームウィンドウ右上部にあるStats(統計データ)から確認できます。 Unity 5ではDraw Callの値はBatchesで表されます (横のSaved by batchingが後述のバッチングにより減少した数です)。
Draw Callは以前からよく速度の指標として用いられていますが、 Unity 5ではシェーダの準備をするSetPass Callの方が指標として適切とされているようです。 この値もStatsから確認できます。
動的バッチングとは?
あらかじめ定められた一連の処理を一度に実行することをコンピュータの世界ではバッチ処理といいます。
Unityにおける(ドローコール)バッチングとは複数の描画命令(Draw Call)をひとつの描画命令にまとめあげることをいいます。 バッチングがうまくいくとDraw Callが減少するため多くの場合に実行速度が改善されます。 これを行うための最も重要な条件はマテリアル(ここではUnityにおけるMaterialオブジェクトではなく単純に材質を表現するデータ群)が同じであることです。 UnityのMaterialオブジェクトは概ねマテリアルを表現しているため、 これを共有しているゲームオブジェクト同士は同時に描画できる最低条件を満たしていることになります。
動的バッチングとは動的(実行時、ランタイム)に行われるバッチングのことです。
頂点属性 (vertex attribute)とは?
頂点属性という訳が正しいかはわかりませんが、 vertex attributeとは頂点ごとに与えられる値のことをいいます(例えば位置や法線、UV等)。
前節にてマテリアルが一致していることがバッチングの条件だといいましたが、 じゃあマテリアル以外の描画用情報ってのはなんなのかというと形状データになります。 UnityにおけるMeshオブジェクトがこれにあたります。 この形状データが持っている情報が頂点と面の情報であり、さらにその頂点が持っている情報が頂点属性になります。
Sphereは動的バッチングができない!?
さて、前置きが長くなりましたが本題に戻ります。
Unity標準のSphereメッシュでは動的バッチングが効きません。 もちろんこれはマテリアルの不一致が原因ではありません。 メッシュ自体に問題があります。
Unityの動的バッチングにはマテリアルの一致以外にいくつかの条件があります。 詳細についてはマニュアルを参照してください。 今回はこれらの条件のうち頂点属性の数が原因となったようです。
マニュアルを見ると、 動的バッチングには頂点ごとにオーバーヘッドがかかるため、 メッシュに含まれる頂点属性の数が900未満のときにのみバッチングされるよと書いてあります *1。 つまりは、動的バッチングの準備にかかる時間の方が稼げる時間よりも長くなるかもしれないから、 複雑なメッシュに対してはバッチングしないよということみたいです。
Sphereメッシュには526もの頂点があるらしく、 例えばこれらが位置と法線の二つの情報を持っていると考えると、 この二倍の頂点属性数をもつことになり1052で900を超えてしまうため動的バッチングの対象外となります。
おわりに
モバイルアプリによって単純なゲームの面白さが再発見されている今日このごろ、 球のモデルというのはテスト以外でも頻繁に使われそうな気がしますが、 大量に生成する場合には自分でローポリの球モデルをインポートしたほうがいいようです。
*1:マニュアルの日本語訳ではこの部分が間違っていて、頂点属性数が頂点数になっています。