Archive for 6月, 2013

レイヤーを可視にするタイミング

Posted 2013.06.28 in 吉里吉里

前回の記事で 『無効領域の発生タイミング』 について説明したが、
それについてさらに重要な事実がある。

レイヤーが不可視の時は無効領域は発生しない!

のである。

動作の確認

前回のスクリプトを次のように修正してみよう。


      // レイヤーを移動
      layer.setPos(100, 100);
      layer.visible = true;

レイヤーを可視にしてから移動していたのを
レイヤーを移動してから可視にするようにした。
これを実行すると…

KiriKiri_setPos3

左上の領域が再描画されなくなった!
作成したばかりのレイヤーは不可視なので、setPos 関数で位置を変更しても無効領域は発生しない。
よって左上の領域が無効領域にならなくなったのである。

いままで可視にしてから移動することで、無駄に無効領域を発生させてしまっていた。
移動させてから可視にすることで無駄を削減することができた!

ちなみにこの場合、left,top プロパティを使った方法でも同様の結果になる。


      // レイヤーを移動
      layer.left = 100;
      layer.top = 100;
      layer.visible = true;

このようなことからレイヤーを可視にするのは、
位置やサイズの調整を全て終えた後、最後に行うべきであると言える。

表示状態が関係ないケース

レイヤーが非表示の場合は無効領域は発生しないことはわかったが、
非表示でも無効領域が発生するケースがある。

absolute, order プロパティの操作は、
対象レイヤー自体の表示状態に関係なく無効領域を発生させるので注意が必要だ。
この時発生する無効領域はちょっと特殊で、
順序を変更するレイヤーとその影響を受けるレイヤーとが重なる領域が無効領域になる。
実質的に順序が変わらない場合、無効領域は発生しない。

動作を確認してみよう。


      // 別のレイヤーを作成
      var other = new Layer(this, primaryLayer);
     add(other);
      other.setImageSize(200, 10);
      other.setSizeToImageSize();

      // レイヤーを移動
      layer.absolute = 100;
      layer.setPos(100, 100);
      layer.visible = true;

順序の変更が生じるように、別のレイヤーを作成している。

KiriKiri_setPos4

レイヤーが不可視であるにも関わらず、左上の領域で妙な無効領域が発生しているのがわかるだろう。
さっき説明したように、これは other と layer が重なっていた部分の領域である。

このように absolute, order プロパティの操作が
レイヤーが不可視でも無効領域を生じてしまうのは、仕様というより実装上の不備かもしれない。
とにもかくにも、このようなことから absolute, order プロパティの操作は
次のようにレイヤーを可視する直前に行うのが無駄がなく効率的だと思われる。


      // レイヤーを移動
      layer.setPos(100, 100);
      layer.absolute = 100;
      layer.visible = true;

親レイヤーが非表示のケース

親レイヤーが非表示の場合、子レイヤーに対する操作は
その子レイヤーの表示状態に関わらず無効領域を生じない。
よって、親レイヤーを可視にするのは子レイヤーを全て作成し終わった後にすべきである。

まとめ

『レイヤーを可視にするタイミング』 は想像以上に重要だ。

「レイヤーを可視にするのは最後にすべき!」

なんて言われなくても
慣習的になんとなく一番最後に visible=true していた人は多いかもしれない。
しかしそのことは、効率面に置いて非常に重大な効果があるのである。


left,top より setPos の方が高速

Posted 2013.06.26 in 吉里吉里

レイヤーの縦横位置を変更するには


layer.left = 100;
layer.top = 100;

とする方法と


layer.setPos(100, 100);

とする方法があるが

setPos の方がはるかに高速である!

2つの方法の違い

この2つの方法はまったく同じ意味に見えるが…
実は2つの方法には大きな違いがある。

left, top の方が関数を呼び出していない分、より高速なのではないかと思うかもしれないが、
left, top はプロパティなので、実質的には関数を2回呼び出していることと同じだ。
よってむしろ setPos の方が関数呼び出し1回で済む点において高速である。

しかしそんなことは些細な違いでしかない。

それよりもずっと大きな、決定的な違いがある。
それは

無効領域の発生タイミング

にまつわる動作の違いである。
ちなみに無効領域とは、画面上において再描画が行われる領域のことである。

動作の確認

動作の違いを確認してみよう。


class TestWindow extends Window
{
  function TestWindow()
  {
    super.Window();
    
    // プライマリレイヤー作成
    var w = 200;
    var h = 200;
    add(new Layer(this, null));
    primaryLayer.setImageSize(w, h);
    primaryLayer.setSizeToImageSize();
    setInnerSize(w, h);
    primaryLayer.fillRect(0, 0, w, h, 0); // 黒
  }
  
  function onKeyDown(key, shift)
  {
    // Enterキーが押されたらレイヤーを作成する
    if(key == VK_RETURN)
    {
      // レイヤーを作成
      var w = 100;
      var h = 100;
      var layer = new Layer(this, primaryLayer);
      add(layer);
      layer.setImageSize(w, h);
      layer.setSizeToImageSize();
      layer.fillRect(0, 0, w, h, 0xFFAAAAAA); // 灰
      
      // レイヤーを移動
      layer.visible = true;
      layer.left = 100;
      layer.top = 100;
    }
  }
}

var win = new TestWindow();
win.visible = true;

これを実行したらまずウィンドウを選択して Shift+F11キー を押し
更新矩形を表示するモードにする。
そして Enterキー を押すと次のようになる。

KiriKiri_setPos1

黄色い枠で示されているのが無効領域となり、再描画が行われた領域である。
細かく区切られているように見えるのは、キャッシュ効率を上げるための描画分割による作用だ。

レイヤーが移動する時、無効領域は2つ発生する。
『レイヤーが移動する前の領域』 と
『レイヤーが移動した後の領域』 だ。
画面の左上の領域が 『レイヤーが移動する前の領域』
画面の右下の領域が 『レイヤーが移動した後の領域』 である。

注目して欲しいのは、なんの関係もない右上の領域が再描画されている点だ。
では、これを setPos に置き換えて実行してみよう。


      // レイヤーを移動
      layer.visible = true;
      layer.setPos(100, 100);

KiriKiri_setPos2

右上の領域が無駄に再描画されなくなった!

何が起きているのか

この動作の違いは、先に述べたように 『無効領域の発生タイミング』 に起因している。

無効領域は、レイヤーに対して
操作を行った直後に発生する。

left, top プロパティで位置を変更する方法の場合
left = 100; とした時点で無効領域が発生し
top = 100; とした時点で無効領域が再度発生するという動作になる。
そう、これによって右上に無駄な無効領域が発生し、無駄な再描画が行われてしまったのだ。

setPos の場合、leftとtopを両方変更してから無効領域が発生する。
よって、無駄な無効領域が発生せず、無駄な再描画も行われない。
最初に「setPos の方がはるかに高速」と言ったのはこのことである。

まとめ

このようなことからレイヤーの縦横位置を同時に変更する時は
left, top プロパティよりも setPos 関数を使うべきだと言えるだろう。