Qtブログ(日本語)

QtWebKit における合成のアクセラレーションに関するレポート

作成者: 鈴木 佑|Nov 18, 2011 8:43:54 AM

この記事は Qt Blog の "QtWebKit Accelerated Compositing Report" を翻訳したものです。
執筆: No'am Rosenthal, 2011年11月10日

昨年の私のブログ記事以降も、QtWebKit の合成のアクセラレーションに関する様々な対応をしてきました。その件に関する問い合わせがいくつかあったため、この記事を書くことにしました。興味を持ってくれる人がいることを祈っています :)

今回の記事は若干内容が高度なため、合成のアクセラレーションについての基礎知識がない方は、Ariya のブログ記事 (もしくは こちら、これも Ariya によるものです)を先に読むことをお勧めします。QtWebKit 内部のグラフィックスアクセラレーションに興味がある方は先に進んでください。

まずはじめに、TextureMapper が最近の trunk ではデフォルトで有効になっています。これは QtWebKit が CSS のアニメーションと変形のみに最適化された、独自の小さなシーングラフを持っていることを意味します。TextureMapper はパブリックな API を持っていないため、CSS シェーダーのプロポーザル や preserves-3d のような特別な 3D 変形のための CSS などの対応が必要になった場合でも、簡単に変更や最適化を進めることができるようになっています。

次に、標準のウェブコンテンツにタイル式のバッキングストアを用いるために多くの時間を割きました。マルチプロセスモデルの WebKit2 では CPU による描画はバックグラウンドのプロセスで行われ、その結果は共有メモリを通して UI プロセスに渡されテクスチャにアップロードされます。このため、この作業は以前にも増して重要になります。ウェブプロセスが文字やパスの描画やその他の時間のかかる作業をしている間でも UI プロセスは固まらなくなります。クロスプロセスのタイル式のバッキングストアの改善により、ユーザーのスクロールの軌跡を解析して、次にスクリーンに表示されるエリアのタイルを描画してしまうようなユーザーエクスペリエンスの最適化を行うことができました。

WebKit2 といえば...

クロスプロセスの環境に対応した合成のアクセラレーションの作業はとても面白い挑戦でした。プラットフォームごとに様々な方法で解決しました。まず始めにアップルの場合は、合成のアクセラレーションはクロスプロセスのアーキテクチャである CoreAnimation の上で動作しています。Chromium では我々がサポートしようと思っているモバイルプラットフォームにはそぐわない、独立した「GPU プロセス」を持っています。ここで解決すべき問題は、実際の合成をどこで行うかということでした。WebKit はレイヤーのツリーとそれぞれのレイヤーの画像のアップデート、それからそれぞれのレイヤーのアニメーションや描画情報を提供してくれます。これらはすべてウェブプロセスで行われます。我々のブラウザや QML の Web ビューでは、ピンチ操作によるズームの変更など、UI プロセスでも GPU を広範囲に渡って使用しています。ウェブプロセスでのウェブページの GPU 処理とユーザー操作による UI プロセスでの GPU 処理をどのように統合すべきでしょうか?

これには2つのオプションがあります:

  1. 複数レイヤーの合成を OpenGL を使用してウェブプロセスで行い、X11 や EGL のようなプロセス間での画像の共有の仕組みを使用してクライアント側のアプリケーションのシーンでその結果を合成する。
  2. すべてのレイヤーのツリー情報と画像の情報を UI プロセスに渡し、UI プロセスで実際の OpenGL の合成を行う。

我々が初めに取った方法は、自然な1の方でした。しかし、モバイルプラットフォーム上では、コンテキストの切り替えが多発したり、同期が困難だったり、フレームレートの低さを克服できないなどの問題がありました。このため、2の方法に方向転換しました。レイヤーのツリーやアニメーションの情報を UI プロセスに渡すため、シリアライズに関する大量のつまらないコードが必要でしたが、それに見合う価値がありました。レイヤーのツリーのセットアップやアニメーションの開始の為に若干のオーバーヘッドが生じますが、一旦それが済んでしまった後は、実際のフレームの合成はすべて UI プロセスで行われ、ユーザーに起因するズームやスクロールなどのジェスチャーも含めて OpenGL を用いた合成は1つの場所で行われます。

これを WebKit2 で行った際、合成のアクセラレーションのパスとタイル式のバッキングストアのパスが違うことが原因で起こる問題がいくつかありました。タイル式のバッキングストアはスクリーン上で「合成処理の行われない」コンテンツにのみ使用し、合成済みのレイヤーをその上に描画していました。これはインターネット上の大半の合成処理のないコンテンツに対してはとても大きな最適化になりますが、いくつかの厄介なバグが生じました。これらのバグは、大きなコンテンツの塊を持ち一般的に合成が必要なローテーションのアニメーションなどのエフェクトを使用している場合に起こりました。ズームの変更時のコンテンツのスケールで描画をくっきりさせるなどの技をタイル式のバッキングストアのコードの一部で行うような工夫をしましたが、合成のアクセラレーションの流れでは一度も実行されませんでした。このため、合成のアクセラレーションが有効かどうかによってくっきりするコンテンツとぼんやりするコンテンツのような差が生じてしまいました。もちろん、これは怪しい動作で、アニメーションの実行をウェブプロセスと UI プロセスの両方で (例えばあるエレメントには "left" のトランジションが、別のエレメントには "-webkit-transform" のトランジションがあるような場合) 行う場合に悲しいことになるのは言うまでもありません。

現在作業をしている trunk のバージョンでは、これに対応するためにさらに1つ上のステップを取りました。合成済みのレイヤーを合成のないのレイヤーの「上に」被せるのではなく、合成のないコンテンツも z-index の小さな1つのレイヤーとして扱うようにして、すべての合成処理をアクセラレーションの利いたプロセスで実行することにしました。これにより、すべてのレイヤーが同期され、タイルも一緒になり、くっきりとしたコンテンツのスケーリングを最背面のレイヤーかどうかに関わらずすべてのレイヤーに適用することが可能になりました。

Qt Quick Scenegraph を使用しない理由

これは何度も聞かれる質問なので、ここで回答しようと思います。QGraphicsView で常に直面する問題は、それが非常に多くの目的で使われていて、様々なユースケースに対応する必要があるため、結局ある1つの目的のための完璧な最適化ができないという点だと思います。名前のとおり、Qt Quick Scenegraph は QML アプリケーションを高速に実行するためにボトムアップで作られたもので、その目的には非常に適しています。CSS 3D/アニメーションのアクセラレーションの場合は、速度以外にも WebKit の CSS のテストをクリアするための複雑なルールが数多く存在します。我々のケースでは、パブリックな API は CSS3 であり、(QGraphcisView や Qt Quick Scenegraph のような)その他のパブリックな API の導入は不要な複雑性が増すだけです。Qt Quick Scenegraph には元々の目的を完全に満たし、CSS の描画のための追加のコードを提供することの2つを望みますが、これは必ずしも両立しません。どちらにしろ、真剣に考えた末での難しい判断でした。この判断は状況の変化次第では再度改めるかもしれません。

今後の予定

様々な問題が山積していて、すでに何人かが解決のためのハッキングをはじめています。以下の私の「TODO」リストが現在の状況を把握するのに役に立つかもしれません。

  1. 変形と不透明度のアニメーション - この部分はアップストリームにはまだ入っていません。現在はすべてのアニメーションのフレームに対してレイヤーのツリー情報を IPC メッセージで渡しています。しかし、これは対応中です。
  2. 動画 – 現在は動画はアクセラレーションの利いた合成とは連携していません。フルスクリーンでの動画再生を「正しく」行ったり、ページ内での動画再生をすべてのフレームのピクセルの読み込み無しにアクセラレーションしたい場合に必要な機能です。
  3. WebGL – これは WebKit2 で解決すべき問題の中でも特に Linux では面白いものです。ウェブプロセスで X11 に描画された WebGL の結果と UI プロセスで実行している GL コンテキストをいかに効率よく合成するかを考えなければいけません。Chromium はすでにこの分野においていくつか興味深い方法をとっているので、それを参考にします。
  4. 反射 – これはまだあまり利用されていない CSS3 の機能で、効率よくサポートするためにはたくさんのコードを要します。現在は合成を伴う反射に関するテストに失敗している状態で、これは今後の課題です。
  5. CSS シェーダー – 現時点ではこの機能は WebKit 自体にも実装されていませんが、それが行われた際にはそれをフックし、WebKit2 のシリアライズを通じて TextureMapper で対応させる必要があります。
  6. その他の要素のダイレクトな合成の調査 – 例えば、シェーダーをテキストやグラデーションなどの適切な場所でそのまま使えるのか?
  7. ちょっとしたテスト、バグ修正、最適化は常に必要で、常に大歓迎です。
  8. 8、8、8番目はなんだっけ。
  9. CSS4 の四次元変換のアクセラレーション

もし合成の可能性を知りたかったり、現在の実装で何ができて何ができていないかを知りたい場合には LayoutTests/compositing をまず見てみるのがいいでしょう。