RenderingNG アーキテクチャ

Chris Harrelson
Chris Harrelson

ここでは、RenderingNG のコンポーネントが レンダリング パイプラインの流れについて説明します。

レンダリングの上位レベルでは、次のタスクを行います。

  1. 画面上のピクセルにコンテンツをレンダリングします。
  2. コンテンツの視覚効果をアニメーション化します。
  3. 入力に応じてスクロールする。
  4. デベロッパー スクリプトや他のサブシステムが応答できるように、適切な場所に効率的に入力をルーティングする。

レンダリングされるコンテンツは、各ブラウザタブのフレーム ツリーと アクセスできます。タッチ スクリーンからの未加工の入力イベントのストリームも マウス、キーボード、その他のハードウェア デバイスに対して有効です。

各フレームには次のものが含まれます。

  • DOM 状態
  • CSS
  • キャンバス
  • 画像、動画、フォント、SVG などの外部リソース

フレームは、HTML ドキュメントとその URL です。 ブラウザタブで読み込まれたウェブページに最上位フレームがある。 最上位レベルのドキュメントに含まれる各 iframe の子フレーム、 継承されます

視覚効果とは、ビットマップに適用されるグラフィック操作のことです。 たとえばスクロール、変換、クリップ、フィルタ、不透明度、ブレンドなどです。

アーキテクチャ コンポーネント

RenderingNG では、これらのタスクは複数のステージとコードに論理的に分割されます。 説明します。コンポーネントは、最終的にさまざまな CPU プロセス、スレッド、 そのスレッド内のサブコンポーネントに ルーティングされますそれぞれが目標の達成において 信頼性スケーラブルなパフォーマンス あらゆるウェブ コンテンツ向けの拡張性を備えています。

パイプライン構造のレンダリング

<ph type="x-smartling-placeholder">
</ph> レンダリング パイプラインの図。
矢印は各ステージの入力と出力を示しています。ステージ 実行されているスレッドやプロセスを示すために、色分けされています。イン ステージは、プロジェクト、プロジェクト、プロジェクト、 2 色にしているのがその理由です。 緑色のステージは、プロセスのメインスレッドをレンダリングします。黄色はレンダリングプロセスのコンポジタです オレンジのステージは可視化のプロセスです。

レンダリングは、多くのステージとアーティファクトが作成されるパイプラインで進行する。 あります。各ステージは、定義された 1 つのタスクを実行するコードを表します。 説明します。アーティファクトはデータ構造 入力または出力です。

ステージは次のとおりです。

  1. アニメーション化: 計算されたスタイルを変更し、宣言型のタイムラインに基づいて、時間の経過とともにプロパティ ツリーを変更します。
  2. スタイル: CSS を DOM に適用し、計算されたスタイルを作成します。
  3. レイアウト: 画面上の DOM 要素のサイズと位置を決定します。 イミュータブル フラグメント ツリーを作成します。
  4. 事前ペイント: コンピューティング プロパティ ツリーと 無効化 必要に応じて、既存のディスプレイ リストと GPU テクスチャ タイルを使用します。
  5. スクロール: プロパティ ツリーを変更することで、ドキュメントとスクロール可能な DOM 要素のスクロール オフセットを更新します。
  6. ペイント: DOM から GPU テクスチャ タイルをラスター化する方法を記述するディスプレイ リストを計算します。
  7. commit: プロパティ ツリーとディスプレイ リストをコンポジタ スレッドにコピーします。
  8. レイヤ化: ディスプレイ リストを合成レイヤリストに分割し、独立したラスタライズとアニメーションを実現します。
  9. ラスター、デコード、ペイント ワークレット: ディスプレイ リスト、エンコードされたイメージ、ペイント ワークレット コードをそれぞれ GPU テクスチャ タイル
  10. Activate: GPU タイルを画面に描画して配置する方法を表すコンポジタ フレームを、視覚効果とともに作成します。
  11. 集約: 表示されているすべてのコンポジター フレームからのコンポジター フレームを、単一のグローバル コンポジター フレームに結合します。
  12. Draw: 集約されたコンポジタ フレームを GPU で実行し、画面上にピクセルを作成します。

レンダリング パイプラインのステージは、不要な場合はスキップできます。 たとえば、視覚効果とスクロールのアニメーションでは、レイアウト、プリペイント、ペイントをスキップできます。 そのため、図ではアニメーションとスクロールに黄色と緑色のドットが付いています。 視覚効果のためにレイアウト、プリペイント、ペイントをスキップできる場合は、 完全にコンポジタ スレッドで実行し、メインスレッドをスキップできます。

ブラウザ UI のレンダリングはここでは直接示していませんが、 同じパイプラインを単純化したものと (実際、その実装ではコードの大部分を共有しています)。 動画(直接描写されていないもの) 通常、フレームを GPU テクスチャ タイルにデコードする独立したコードでレンダリング コンポジタ フレームと描画ステップに接続されます。

プロセスとスレッドの構造

CPU プロセス

複数の CPU プロセスを使用することで、パフォーマンスとセキュリティの分離を実現 ブラウザの状態からの情報、 安定性とセキュリティを GPU ハードウェアから分離します。

CPU プロセスのさまざまな部分の図

  • レンダリング プロセスは、入力内容のレンダリング、アニメーション化、スクロール、ルーティングを行います。 1 つのサイトとタブの組み合わせですレンダリング プロセスがいくつかあります。
  • ブラウザ プロセスがブラウザ UI への入力をレンダリング、アニメーション化、ルーティングする (アドレスバー、タブのタイトル、アイコンを含む)、残りのすべてのルート 適切なレンダリング プロセスに送ります。ブラウザ プロセスは 1 つです。
  • Viz プロセス: 複数のレンダリング プロセスからの合成を集約する ブラウザプロセスも含まれますGPU を使用してラスター化と描画を行います。Google Viz プロセスが 1 つになります。

異なるサイトは常に レンダリングプロセスの違いです

通常、同じサイト内に複数のブラウザタブやウィンドウがあると、レンダリングは異なる すべてのプロセスに適用されます。ただし、タブが 開きます。 パソコンのメモリ負荷が高い場合、Chromium で複数のタブを配置することがある 同じサイトから同じレンダリング プロセスに移動できます。

1 つのブラウザタブで 異なるサイトのフレームは常に互いに異なるレンダリング プロセスで使用されるため、 同じサイトからのフレームは常に同じレンダリング プロセスになります。 レンダリングの観点からは 複数のレンダリングプロセスを使うことの 重要な利点として タブとタブでパフォーマンスの分離を実現 相互に通信します。 さらに、オリジンではさらなる分離にオプトインできます。

Viz プロセスは通常 1 つだけなので、Chromium 全体で 描画に使用する 1 つの GPU と画面です。

Viz を独自のプロセスに分離すると、バグが発生しても安定性が増します。 GPU ドライバまたはハードウェア。セキュリティの分離や これは GPU API で重要な点です Vulkanセキュリティ全般について説明します。

ブラウザには多数のタブとウィンドウがあるため、 いずれもブラウザ UI のピクセルを 描画するためのピクセルを備えています なぜブラウザ プロセスが 1 つしかないのか、疑問に思われるかもしれません。 なぜなら、一度にフォーカスできるのはそのうちの 1 つだけだからです。 実際、非表示のブラウザタブのほとんどは無効になり、GPU メモリがすべて削除されます。 ただし、複雑なブラウザ UI レンダリング機能の実装は増加の一途をたどっています。 レンダリング プロセスでもよく使用されます( WebUI)。 これはパフォーマンスの分離のためではなく Chromium のウェブ レンダリング エンジンの使いやすさを活用するために、

古い Android デバイスの場合: WebView で使用される場合、レンダリングとブラウザのプロセスが共有されます。 (これは Android の Chromium には当てはまらず、WebView だけに当てはまります)。 WebView では、ブラウザ プロセスもエンベディング アプリと共有され、 WebView にはレンダリング プロセスが 1 つしかありません。

保護された動画コンテンツをデコードするためのユーティリティ プロセスが存在する場合もあります。 このプロセスは前の図には示されていません。

スレッド

スレッドは、タスクが低速であっても、パフォーマンスの分離と応答性の実現に役立ちます。 多重バッファリングにも対応しています

レンダリング プロセスの図。

  • メインスレッドは、スクリプト、レンダリング イベントのループ、ドキュメントのライフサイクル、 ヒットテスト、スクリプト イベントのディスパッチ、HTML、CSS などのデータ形式の解析。
    • メインスレッド ヘルパー。エンコードやデコードが必要な画像ビットマップや blob の作成などのタスクを実行します。
    • ウェブワーカー OffscreenCanvas の レンダリングイベントループが含まれます
  • コンポジタ スレッドは入力イベントを処理します。 ウェブ コンテンツのスクロールとアニメーションを実行します。 ウェブ コンテンツのレイヤ化を最適に計算し、 画像デコード、ペイント ワークレット、ラスタータスクの調整です。
    • コンポジタ スレッド ヘルパー: Viz ラスタータスクを調整します。 画像デコード タスクの実行、ワークレットのペイント、フォールバック ラスターの実行が可能です。
  • メディア、デマルチプレクサー、オーディオ出力スレッドのデコード 動画や音声ストリームの処理と同期ができます。 (動画はメインのレンダリング パイプラインと並行して実行されます)。

メインスレッドとコンポジタ スレッドの分離は、 パフォーマンスの分離 メインスレッドからのスクロール動作です

メインスレッドはレンダリングプロセスごとに 1 つだけです 同じサイトからの複数のタブやフレームが最終的に同じプロセスになる場合があります。 ただし、さまざまなブラウザ API で実行される作業からパフォーマンスが分離されます。 たとえば、Canvas API での画像ビットマップと blob の生成は、メインスレッドのヘルパー スレッドで実行されます。

同様に、コンポジタ スレッドはレンダリング プロセスごとに 1 つだけです。 一般的に、1 つしかないことは問題ではなく、 なぜなら コンポジタースレッドでの コストの高い操作はすべて コンポジタ ワーカー スレッドまたは Viz プロセスに委任されます。 この作業は、入力ルーティング、スクロール、アニメーションと並行して行うことができます。 コンポジタ ワーカー スレッドは、Viz プロセスで実行されるタスクを調整します。 しかし GPU アクセラレーションはどこでも Chromium で制御できない理由で失敗する場合があります。 デバッグできます このような場合、ワーカー スレッドは CPU のフォールバック モードで処理を実行します。

コンポジタ ワーカー スレッドの数は、デバイスの機能によって異なります。 たとえばデスクトップでは一般に モバイル デバイスに比べて CPU コア数が多く、バッテリーへの制約が少ないためです。 これは スケールアップとスケールダウンです。

レンダリング プロセスのスレッド アーキテクチャは、 最適化パターン:

  • ヘルパー スレッド: 長時間実行サブタスクを追加のスレッドに送信し、 親スレッドが他の同時リクエストに応答するようになります。メインスレッド ヘルパー スレッドやコンポジタ ヘルパー スレッドなどが、この手法の好例です。
  • 複数バッファリング: 新しいコンテンツのレンダリング中に以前にレンダリングされたコンテンツを表示し、 レイテンシが高くなります。コンポジタ スレッドはこの手法を使用します。
  • パイプラインの並列化: 複数の場所でレンダリング パイプラインを実行する できます。これにより、スクロールとアニメーションが高速になります。たとえ メインスレッドのレンダリング更新が行われているときに、スクロールとアニメーションが 並列して実行されます

ブラウザのプロセス

レンダリングおよび合成スレッドと、レンダリングおよび合成スレッド ヘルパーの関係を示すブラウザのプロセス図。

  • レンダリングと合成スレッドは、ブラウザ UI での入力に応答します。 他の入力を適切なレンダリング プロセスにルーティングする。ブラウザ UI のレイアウトとペイントを行います。
  • レンダリングと合成スレッド ヘルパー 画像デコード タスクの実行と、フォールバック ラスターまたはデコードを行います。

ブラウザ プロセスのレンダリングと合成スレッドは レンダリング プロセスのコードや機能だけでなく、 メインスレッドとコンポジタ スレッドが 1 つに統合されています。 この場合に必要なスレッドは 1 つだけです。 長いメインスレッド タスクからパフォーマンスが分離され、 何もないからです

Viz のプロセス

Viz プロセスには、GPU メインスレッドとディスプレイ コンポジタ スレッドが含まれます。

  • GPU メインスレッドは、ディスプレイ リストと動画フレームを GPU テクスチャ タイルにラスターします。 画面にコンポジタ フレームを描画します。
  • ディスプレイ コンポジタ スレッドは、各レンダリング プロセスからの合成を集約して最適化します。 単一のコンポジタ フレームに変換して画面に表示できます。

ラスターと描画は通常同じスレッド上で行われますが どちらも GPU リソースを使用するため GPU のマルチスレッド使用を確実に行うことは (GPU へのより簡単なマルチスレッド アクセスが、新しい Vulkan 標準など)。 Android WebView には、描画用の OS レベルのレンダリング スレッドが別途用意されている WebView がネイティブ アプリに埋め込まれるためです。 他のプラットフォームにも今後このようなスレッドが存在する可能性があります。

ディスプレイ コンポジタは常にレスポンシブである必要があるため、別のスレッドにあります。 GPU メインスレッドで速度が低下する原因となる可能性のある原因をブロックしません。 GPU メインスレッドの速度が低下する原因の 1 つは、Chromium 以外のコードの呼び出しです。 (ベンダー固有の GPU ドライバなど)で、予測が困難な方法で処理が遅くなる可能性があります。

コンポーネントの構造

各レンダリング プロセスのメインスレッドまたはコンポジタ スレッド内で、 論理ソフトウェア コンポーネントがあり、構造化された方法で相互に作用します。

レンダリング プロセスのメインスレッド コンポーネント

Blink レンダラの図。

Blink レンダラでの場合:

  • ローカル フレーム ツリー フラグメントは、ローカル フレームのツリーとフレーム内の DOM を表します。
  • DOM と Canvas API コンポーネントには、これらすべての API の実装が含まれています。
  • ドキュメント ライフサイクル ランナーは、commit ステップまでのレンダリング パイプライン ステップを実行します。
  • 入力イベントのヒットテストとディスパッチ コンポーネントでは、以下に対してヒットテストが実行されます。 イベントのターゲットとなる DOM 要素を特定し、入力イベントを実行する ディスパッチ アルゴリズムとデフォルトの動作

レンダリング イベントループのスケジューラとランナーがイベントで実行する内容を決定する 必要があります。デバイスと一致する頻度でレンダリングが行われるようにスケジュールします。 表示されます。

フレームツリーの図。

ローカル フレームツリー フラグメントは少し複雑です。 フレームツリーはメインページとその子 iframe で再帰的に発生することを思い出してください。 フレームがレンダリング プロセス内でレンダリングされる場合、フレームはレンダリング プロセスに対してローカルになります。 それ以外はリモートです

レンダリング プロセスに応じてフレームに色を付けることができます。 上の画像では、緑色の円は 1 つのレンダリング プロセス内のすべてのフレームを示しています。 オレンジ色が 1 番目、青色が 3 番目です

ローカル フレームツリー フラグメントは、フレームツリー内の同じ色の連結コンポーネントです。 この画像には 4 つのローカル フレームツリーがあります。2 つはサイト A 用、1 つはサイト B 用、1 つはサイト C 用です。 各ローカル フレームツリーは、独自の Blink レンダラ コンポーネントを取得します。 ローカル フレームツリーの Blink レンダラのレンダリング プロセスが異なることもある 他のローカル フレームツリーと同様に処理されます。前述のように、レンダリング プロセスの選択方法によって決まります。

レンダリング プロセスのコンポジタ スレッド構造

レンダリング プロセスのコンポジタ コンポーネントを示す図。

レンダリング プロセスのコンポジタ コンポーネントには、次のものがあります。

  • 合成されたレイヤリスト、表示リスト、プロパティ ツリーを管理するデータハンドラ
  • ライフサイクル ランナー: アニメーション、スクロール、複合、ラスター、 デコードと有効化を行います (アニメーションとスクロールは、メインスレッドとコンポジタの両方で行うことができます)。
  • 入力テストハンドラとヒットテストハンドラ: 合成レイヤの解像度で入力処理とヒットテストを実行します。 スクロール操作をコンポジタ スレッドで実行できるかどうかを判断し、 ヒットテストのターゲットを指定します

実際のアーキテクチャ例

この例では、3 つのタブがあります。

タブ 1: foo.com

<html>
  <iframe id=one src="foo.com/other-url"></iframe>
  <iframe  id=two src="bar.com"></iframe>
</html>

タブ 2: bar.com

<html>
 …
</html>

タブ 3: baz.com html <html> … </html>

これらのタブのプロセス、スレッド、コンポーネントの構造は次のようになります。

タブのプロセスの図。

レンダリングの 4 つの主要なタスクについて、それぞれ例を 1 つずつ見ていきましょう。 留意点:

  1. コンテンツを画面上のピクセルにレンダリングします。
  2. コンテンツの視覚効果をある状態から別の状態にアニメーション化します。
  3. 入力に応じてスクロールする。
  4. デベロッパー スクリプトや他のサブシステムが応答できるように、入力を適切な場所に効率的にルーティングします。

タブ 1 の変更した DOM をレンダリングするには:

  1. 開発者スクリプトは、foo.com のレンダリング プロセスで DOM を変更します。
  2. Blink レンダラは、レンダリングを行う必要があることをコンポジタに伝えます。
  3. コンポジタは、レンダリングを行う必要があることを Viz に伝えます。
  4. Viz はレンダリングの開始をコンポジタに返します。
  5. コンポジタは開始シグナルを Blink レンダラに転送します。
  6. main スレッドのイベント ループランナーは、ドキュメントのライフサイクルを実行します。
  7. メインスレッドが結果をコンポジタ スレッドに送信します。
  8. コンポジタ イベント ループ ランナーは、合成ライフサイクルを実行します。
  9. ラスタータスクはすべてラスター用の Viz に送信されます(多くの場合、これらのタスクは複数あります)。
  10. Viz は GPU 上でコンテンツをラスター化します。
  11. Viz はラスタータスクが完了したことを通知します。 注: Chromium ではラスターの作成が終わるのを待たないことが多いため、 代わりに トークンを同期 解決が必要な問題 ステップ 15 が実行される前に
  12. コンポジタ フレームが Viz に送信されます。
  13. Viz は、foo.com レンダリング プロセスのコンポジタ フレームを集約し、 bar.com iframe レンダリングプロセス ブラウザの UI です
  14. Viz が抽選のスケジュールを設定します。
  15. Viz が、集約されたコンポジタ フレームを画面に描画します。

タブ 2 の CSS 変換遷移をアニメーション化するには:

  1. bar.com レンダリング プロセスのコンポジタ スレッドがアニメーションをオンにする 既存のプロパティ ツリーを変更することで、コンポジタ イベントループで既存のプロパティを再作成できます。 これにより、コンポジタのライフサイクルが再実行されます。(ラスタータスクとデコードタスクが発生する場合もありますが、ここでは説明しません)。
  2. コンポジタ フレームが Viz に送信されます。
  3. Viz は、foo.com レンダリング プロセス、bar.com レンダリング プロセス、ブラウザ UI のコンポジタ フレームを集約します。
  4. Viz が抽選のスケジュールを設定します。
  5. Viz が、集約されたコンポジタ フレームを画面に描画します。

タブ 3 のウェブページをスクロールするには:

  1. 一連の input イベント(マウス、タップ、キーボード)がブラウザ プロセスに到着します。
  2. 各イベントは、baz.com のレンダリング プロセスのコンポジタ スレッドにルーティングされます。
  3. コンポジタは、メインスレッドがイベントを認識する必要があるかどうかを判断します。
  4. 必要に応じて、イベントがメインスレッドに送信されます。
  5. メインスレッドが input イベント リスナーを起動する (pointerdowntouchstarpointermovetouchmovewheel) リスナーがイベントで preventDefault を呼び出すかどうかを確認します。
  6. メインスレッドは、preventDefault がコンポジタに呼び出されたかどうかを返します。
  7. イベントがない場合、入力イベントはブラウザ プロセスに返されます。
  8. ブラウザ プロセスが他の最近のイベントと組み合わせてスクロール操作に変換します。
  9. スクロール操作が再び baz.com のレンダリング プロセスのコンポジタ スレッドに送信されます。
  10. そこにスクロールが適用され、bar.com のコンポジタ スレッドが適用されます。 レンダリング プロセスにより、コンポジタ イベントループでアニメーションが起動します。 これにより、プロパティ ツリーのスクロール オフセットが変更され、コンポジタのライフサイクルが再実行されます。 また、メインスレッドに scroll イベントを発行するように指示します(ここでは示していません)。
  11. コンポジタ フレームが Viz に送信されます。
  12. Viz は、foo.com レンダリング プロセスのコンポジタ フレームを集約し、 bar.com のレンダリングプロセス ブラウザの UI です
  13. Viz が抽選のスケジュールを設定します。
  14. Viz が、集約されたコンポジタ フレームを画面に描画します。
で確認できます。

タブ 1 の iframe #two のハイパーリンクで click イベントをルーティングするには:

  1. input イベント(マウス、タップ、キーボード)がブラウザ プロセスに到着します。 近似ヒットテストを実行して bar.com iframe レンダリング プロセスがクリックを受信するべきことを確認し、クリックをそこで送信します。
  2. bar.com のコンポジタ スレッドが click イベントをメインスレッドにルーティングする レンダリング イベント ループタスクをスケジュール設定して処理します。
  3. bar.com のメインスレッドのヒットテストの入力イベント プロセッサで、 iframe 内の DOM 要素がクリックされ、スクリプトが監視するための click イベントが発生します。 preventDefaultが聞こえない場合は、ハイパーリンクに移動します。
  4. ハイパーリンクのリンク先ページが読み込まれると、新しい状態がレンダリングされます。 「レンダリング変更された DOM」と使用します。 (この後の変更はここでは示していません)。

重要なポイント

レンダリングの仕組みを覚えて理解するには、多くの時間がかかります。

最も重要なポイントはレンダリング パイプラインです。 細部へのこだわりがモジュール化され、 自己完結型のコンポーネントですこれらのコンポーネントは、並列処理で 最大限に活用するためのプロセスとスレッドを スケーラブルなパフォーマンス 拡張性の機会が得られます。

各コンポーネントは、Google Cloud サービスのパフォーマンスと 最新のウェブアプリに 対応できます

主なデータ構造の全文をお読みください。 コード コンポーネントと同様に RenderingNG にとって重要です。


イラスト: Una Kravets。