cocos2d-x v3における、Zオーダーとオートバッチの関係

More than 3 years have passed since last update.

cocos2d-x v3から、ドローコールのオートバッチ機能が追加されました。

これによりSpriteBatchNodeを使わなくてもドローコールを纏められるようになり、

ドローコールを意識しなくてもパフォーマンスが良くなるようになりました。

そのため、ヒエラルキーに制限が生じてしまうSpriteBatchNodeを使うことは非推奨となりました。

しかしながら、このオートバッチの仕組みを知らないとドローが増えることに直結します。

特にスマホでは、数十程度のドローに抑えないとパフォーマンスが悪化します。

そこでcocos2d-x v3における、ドローコールの発行の流れ、オートバッチの仕組み、そしてZオーダーとの関係について説明したいと思います。

(質問・ツッコミ大歓迎です)


ドローコール発行の流れ

ドローコールが発行されるまでに以下のような流れになります。


  1. Sceneから子を深さ優先で探索する

  2. 各Nodeでドローコールを発行するコマンドをレンダラーキューに貯める

  3. レンダラーキューを、後述するグローバルZに従ってソートする

  4. 前後のドローコールでオートバッチを試みる

  5. ドローコール発行してドロー

2系までは(2)の時点でドローコールが発行されてたので、オートバッチ機能のために一旦溜めるようになった形だと思います。

そして、肝になるのは、(3)のソートの方法と、(4)のオートバッチされるために必要な条件です。


オートバッチに必要な条件

ドローコールがバッチされるためには、おおまかに以下の条件を満たす必要があります。


  • テクスチャが同一

  • BlendFuncが同一

他にも若干細かい話がありますが、基本的には上の条件を満たしてるドローコールが連続していればバッチされます。


2種類のZオーダーと描画順

あとはオートバッチされるべきドローコールを、いかに連続して送り込むかです。

ここでは、レンダラーがキューを以下にソートするかについて説明します。

まず、v3からは既存のローカルZの他にグローバルZという概念が導入されました。

グローバルZのおかげでエフェクトなどをヒエラルキーに依存しないで描画順を指定できるようになりました。

しかしながら逆にZオーダーの指定方法が2種類あるために、実際の描画順が分かりづらくなっています。

そこでまず、グローバルZと描画順の関係について解説したあと、ローカルZと描画順の関係について解説します。


レンダラーキューにおけるソート

ローカルZとグローバルZが混在するなかで、レンダラーキューは以下のようにドローコールをソートします。

(前提として、それぞれのZはデフォルト0です)


  1. ドローコールのグローバルZが負、0、正の3つグループに分ける

  2. グローバルZが負と正のグループについては、昇順にソートする

  3. グローバルZが0のグループについては、ソートしない(デフォルトはこれ)

  4. グローバルZが負、0、正の順に結合(したように見せる)

このように、グローバルZを指定したNodeについては、ヒエラルキーに関係なく、(グループ分けがあるものの)小さい順に描画されます。


ローカルZとドローコールの発行順の関係

前述のように、グローバルZが0、つまりデフォルトの指定ではドローコールはソートされません。

つまりレンダラーキューに追加された順にドローコールが溜まっています。

それではどういう順番でドローコールがキューに追加されているかというと、これまた最初に説明した通り深さ優先探索ですが、若干説明を端折っていますのでここで詳しく説明します。

まずSceneオブジェクトをルートとし、そこから深さ優先探索しますが、探索順序に関してはローカルZが深く影響していきます。

まず、ドローコールをキューに追加しているのはNode::visitメソッドですが、これは以下のようなアルゴリズムになっています。


  1. (必要に応じて)自身の子をローカルZの昇順でソートする。(ローカルZが同じ場合は、子に追加した順)

  2. ローカルZが負のものについて、値が小さいものからvisitを呼ぶ

  3. 自身のドローコールを発行する

  4. ローカルZが0以上の子について、順にvisitを呼ぶ

ローカルZが負の場合が若干ややこしいですが、基本的には深さ優先探索と思って構わないと思います。


ポイント


  • グローバルZが0以外のドローコールは昇順ソートだが、破壊的なので同じグローバルZの場合のドローコールの発行順は不透明


    • オートバッチの条件を満たさないドローコールを同じグローバルZで指定すると、ドローが増える可能性あり



  • デフォルトはグローバルZが0なので、ソートされない


    • ヒエラルキーとローカルZが大事

    • ローカルZが負のNodeが、自身のドローコールより優先されるのに注意




オートバッチの対象外となるNode

幾つかの特殊ノードについては、オートバッチの対象とならずに、独自のドローを行います。

これらを使う際は、Zオーダーやヒエラルキーに注意しましょう。


  • LayerColor

  • TextureAtlas


    • Label



  • ProgressTimer

  • DrawNode

  • MotionStreak

  • ClippingNode




例1

同じテクスチャのSpriteであるAとBの間に、同じテクスチャのProgressTimerであるCを挟んだ。



AとBはバッチされません。


例2

同じテクスチャのLabelを並べた。



バッチされません。(回避不可能)


簡単な問題

ここで簡単な問題を出してみたいと思います。


問題

cocos2d-x_Zorder_problem.png


(汚くてすいません・・・)

cocos2d-x_Zorder_answer.png


余談

将来的にレンダラーはマルチスレッドになる計画です。


参考