LoginSignup
50
48

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-07-17

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

余談

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

参考

50
48
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
48