MonaGUI の描画遅い問題

Facbook viewer アプリの表示速度に遅い。ちらつく場合もある。GUI システムにもきちんと踏み込んで調べてみた。
Facebook::repaint() が 60-120 msec ほどかかっていてそれが遅いと感じられる原因。
repaint() の流れは以下のとおり

Facebook::repaint()
  Frame::repaint() : __g にウィンドウの枠などを描く
    Container::repaint()
      Container::update()
        Window::update() : GUI server に MSG_DRAW_WINDOW. ここで共有メモリから VRAM への描画発生 (*1)
      for each Component in Container
        Component::paint() : 部品を描画
        Component::update()
          parent の Grapics に自分をコピー
          parent の update() 通常これは Window::update() (*1)

いかにも遅そうなのは (*1) と (*2) の部分。特に (*2) はひどくて部品の回数だけ、親のコンテナがフルに再描画される。
上記の仮説のもとに計測するとやはりこの部分がボトルネックになっているようだ。コードに手を入れて Window::update() が呼び出される回数が 1/5 や 1/10 にしてみて仮説が正しいことを確認。


とわけで高速化方針は2つ。
・不要に呼ばれている部分を減らす
・上記で足りないなら Window::update の中身をチューニングする

不要に呼ばれている部分を減らす

これは簡単で parent の update を呼ばない repaint を内部用に用意してやれば良い。
その結果 repaint() は 30-50 msec となった。明らかに速くなり十分満足できるレベル。

Window::update の中身をチューニングする

あっさり速くなったので余力がある。ということで軽く調べていくとこんなコードがあっていかにも速くなりそう。

for (int yy = y1; yy < y2; yy++)
{
    int pos = x1 + yy * sw;
    unsigned int* pSBuf = &screen_buffer->Data[pos];
    unsigned int* pVBuf = &vram_buffer->Data[pos];
    uint8_t* pVram = &vram[pos * bypp];
    for (int xx = x1; xx < x2; xx++, pSBuf++, pVBuf++, pVram += bypp)
    {
        if (*pVBuf == *pSBuf) continue;

        *pVBuf = *pSBuf;
        uint8_t* p = (uint8_t*)pSBuf;
        switch (bpp)
        {
            case 8: // broken
                *pVram = (p[0] + p[1] + p[2]) / 3;
                break;
            case 16: // 565
                *(unsigned short*)pVram = Color::bpp24to565(p);
                break;
            case 24:
                pVram[0] = p[0];
                pVram[1] = p[1];
                pVram[2] = p[2];
                break;
            case 32:
                *(unsigned int*)pVram = *pSBuf;
                break;
        }
    }
}

上のコードは VRAM へデータをコピーする。その際に bpp (bits per pixel) に応じて処理を変えている。switch (bpp) の部分。bpp はループの内側で不変なのでもったいない。
これを改善すると repaint() は 10 msec になった。めでたしめでたし。