RenderingNG の詳細: BlinkNG

Stefan Zager
Stefan Zager
Chris Harrelson
Chris Harrelson

Blink とは、Chromium のウェブ プラットフォームの実装を指し、コンポジット前のレンダリング フェーズがすべて含まれ、最終的にコンポジター commit になります。点滅のレンダリング アーキテクチャについて詳しくは、このシリーズの前の記事をご覧ください。

BlinkWebKit のフォークとしてスタートしました。WebKit 自体は 1998 年に設立された KHTML のフォークです。Chromium で最も古い(かつ最も重要な)コードの一部が含まれ、2014 年には間違いなくその年代を示しています。その年、私たちは BlinkNG と名付けられた一連の野心的なプロジェクトに着手しました。その目的は、Blink コードの体系と構造における長年の欠陥に対処することを目標に掲げたことです。この記事では、BlinkNG とその構成プロジェクトについて、Google が開発した理由、達成したこと、設計を形成した指針、そして将来的な改善の可能性について紹介します。

BlinkNG の前後のレンダリング パイプライン。

NG 前のレンダリング

Blink 内のレンダリング パイプラインは常に概念的に複数のフェーズ(スタイルレイアウトペイントなど)に分割されていましたが、抽象化の障壁がリークしていました。大まかに言うと、レンダリングに関連するデータは、存続期間が長い可変オブジェクトで構成されます。これらのオブジェクトはいつでも変更可能で、また、その後のレンダリング更新で頻繁にリサイクルされ、再利用されていました。次のような単純な質問に確実に答えることは不可能でした。

  • スタイル、レイアウト、ペイントの出力を更新する必要があるかどうか。
  • これらのデータが「最終版」になるのはいつか?
  • これらのデータを変更してもよいのはどのような場合ですか?
  • このオブジェクトはいつ削除されますか?

これには次のような例が多数あります。

Style は、スタイルシートに基づいて ComputedStyle を生成します。ただし、ComputedStyle は不変ではありませんでした。後のパイプライン ステージで変更される場合もあります。

StyleLayoutObject のツリーを生成し、layout によってそれらのオブジェクトにサイズと位置の情報に関するアノテーションを付けます。場合によっては、レイアウトによってツリー構造が変更されることもあります。「layout」の入力と出力が明確に分離されていませんでした。

スタイルは、合成の過程を決定する補助的なデータ構造を生成し、それらのデータ構造は、スタイル後の各フェーズで所定の位置に修正されました。

下位レベルでは、レンダリング データ型は主に専用ツリー(DOM ツリー、スタイルツリー、レイアウト ツリー、ペイント プロパティ ツリーなど)で構成されます。レンダリング フェーズは再帰的ツリーウォークとして実装されます。理想的には、ツリーウォークを含む必要があります。特定のツリーノードを処理するとき、そのノードをルートとするサブツリー外の情報にはアクセスすべきではありません。これは、RenderingNG 以前では真実ではありませんでした。ツリーは、処理対象ノードの祖先から頻繁にアクセスされる情報をウォークします。そのため、システムは非常に脆弱で、エラーが発生しやすい状態になっていました。木の根元以外の場所からでも、ツリーの散歩は不可能でした。

最後に、レンダリング パイプラインには、JavaScript によってトリガーされる強制レイアウト、ドキュメントの読み込み中にトリガーされる部分更新、イベント ターゲティングに備えるための強制更新、ディスプレイ システムからリクエストされるスケジュール設定された更新、テストコードのみに公開される特殊な API など、レンダリング パイプラインへの入口が数多くありました。レンダリング パイプラインには、再帰パスとリエントラント パスがいくつかありました(ステージの途中からステージの先頭に移動するなど)。これらのオンランプにはそれぞれ独自の動作があり、場合によってはレンダリングの更新がトリガーされた方法によってレンダリングの出力が依存することもあります。

変更点

BlinkNG は、前述のアーキテクチャ上の欠陥を排除するという共通の目標を持つ、大小さまざまなサブプロジェクトで構成されています。これらのプロジェクトでは、レンダリング パイプラインを実際のパイプラインに近づけるための指針をいくつか共有しています。

  • 一貫した入力点: 常に最初にパイプラインを開始する必要があります。
  • 機能ステージ: 各ステージは明確に定義された入力と出力を持つ必要があり、その動作は機能的である(決定論的で反復可能)必要があります。また、出力は定義された入力にのみ依存する必要があります。
  • 定数入力: ステージの実行中に、どのステージの入力も実質的に一定である必要があります。
  • 不変の出力: ステージが終了すると、残りのレンダリング更新でステージの出力が不変になります。
  • チェックポイントの整合性: 各ステージの終了時点で、それまでに生成されたレンダリング データは自己整合状態になっている必要があります。
  • 作業の重複除去: それぞれのものを 1 回だけ計算します。

BlinkNG のサブプロジェクトをすべて列挙すると、読むのに面倒な作業になりますが、次のような結果になります。

ドキュメントのライフサイクル

DocumentLifecycle クラスは、レンダリング パイプライン全体の進行状況を追跡します。これにより、前述の不変条件を適用する次のような基本的なチェックを実行できます。

  • ComputedStyle プロパティを変更する場合は、ドキュメントのライフサイクルを kInStyleRecalc にする必要があります。
  • DocumentLifecycle の状態が kStyleClean 以降の場合、接続されたすべてのノードに対して NeedsStyleRecalc()false を返す必要があります。
  • ペイント ライフサイクル フェーズに入るときは、ライフサイクルの状態を kPrePaintClean にする必要があります。

BlinkNG の実装の過程で、これらの不変条件に違反したコードパスを体系的に排除し、回帰を避けるためにコード全体にさらに多くのアサーションを散布しました。

低レベルのレンダリング コードを見て、ラビットホールに迷ったことがある方は、「どうしてこんなに来たのだろう?」と自問するかもしれません。前述のように、レンダリング パイプラインにはさまざまなエントリ ポイントがあります。以前は、再帰呼び出しパスや再入呼び出しパスのほか、最初から開始するのではなく中間フェーズでパイプラインに入る場所が含まれていました。BlinkNG の過程で、これらの呼び出しパスを分析し、これらはすべて次の 2 つの基本的なシナリオに削減できると判断しました。

  • ディスプレイ用の新しいピクセルを生成する場合や、イベント ターゲティングのヒットテストを行う場合など、すべてのレンダリング データを更新する必要があります。
  • レンダリング データをすべて更新しなくても回答できる、特定のクエリに関する最新の値が必要です。これには、ほとんどの JavaScript クエリ(node.offsetTop など)が含まれます。

レンダリング パイプラインへのエントリ ポイントは 2 つのみになりました。これらは、この 2 つのシナリオに対応しています。再入可能なコードパスは削除またはリファクタリングされているため、中間フェーズからパイプラインに入ることはできなくなりました。これにより、レンダリングの更新がいつ、どのように行われるかに関する多くの謎が取り除かれ、システムの動作について推論が非常に簡単になりました。

パイプライン スタイル、レイアウト、プリペイント

ペイント前のレンダリング フェーズでは、全体として次の処理を行います。

  • スタイル カスケード アルゴリズムを実行して、DOM ノードの最終的なスタイル プロパティを計算する。
  • ドキュメントのボックス階層を表すレイアウト ツリーを生成する。
  • すべてのボックスのサイズと位置情報を決定する。
  • ペイントのために、サブピクセル ジオメトリをピクセル境界全体に合わせて丸めたりスナップしたりします。
  • 合成レイヤのプロパティ(アフィン変換、フィルタ、不透明度、その他 GPU アクセラレーションが可能なもの)を特定する。
  • 前回のペイント フェーズから変更され、ペイントまたは再ペイントする必要があるコンテンツを特定する(ペイント無効化)。

このリストに変更はありませんが、BlinkNG 以前は、この作業の多くがアドホックな方法で行われており、複数のレンダリング フェーズに分散し、多くの重複機能と非効率性が組み込まれています。たとえば、ノードの最終的なスタイル プロパティの計算は主に style フェーズによって行われてきましたが、style フェーズが完了するまで、最終的なスタイル プロパティの値を決定できない特殊なケースもいくつかありました。レンダリング プロセスには、スタイル情報が完全で不変であると確実に言えるフォーマルなポイントや強制可能なポイントはありませんでした。

BlinkNG 以前の問題を示すもう一つのわかりやすい例は、ペイントの無効化です。以前は、ペイントに至るまでのすべてのレンダリング フェーズで、ペイントの無効化が散らばっていました。スタイルやレイアウトのコードを変更する際、ペイント無効化ロジックにどのような変更が必要かを把握するのは困難でした。また、無効化の過小または過剰なバグにつながるミスが起こりやすくなっていました。古いペイント無効化システムの複雑さについては、LayoutNG に関するこのシリーズの記事をご覧ください。

ペイントのためにサブピクセル レイアウト ジオメトリをピクセル境界全体にスナップする例は、同じ機能を複数実装し、多くの冗長な作業を行った例です。ペイント システムで使用されるピクセル スナップ コードパスは 1 つで、ペイント コード外でピクセル スナップされた座標を 1 回限りのオンザフライ計算で計算する必要がある場合、まったく別のコードパスが使用されていました。言うまでもなく、各実装にはそれぞれ独自のバグがあり、結果が常に一致するとは限りませんでした。この情報はキャッシュ保存されないため、まったく同じ計算が繰り返し行われることがあり、パフォーマンスへの負荷が増大します。

ここでは、ペイント前のレンダリング フェーズにおけるアーキテクチャ上の欠陥を排除した重要なプロジェクトをいくつか紹介します。

Project Squad: スタイル化フェーズのパイプライン化

このプロジェクトは、スタイル フェーズにおける 2 つの主な欠陥に対処し、パイプラインのきれいなパイプライン化を妨げていました。

スタイル フェーズには主に 2 つの出力があります。ComputedStyle には、DOM ツリーに対して CSS カスケード アルゴリズムを実行した結果が含まれます。レイアウト フェーズのオペレーションの順序を確立する LayoutObjects のツリー。概念的には、カスケード アルゴリズムの実行はレイアウト ツリーの生成前に厳密に行う必要があります。以前はインターリーブされていましたProject Squad は、これら 2 つを異なる連続的なフェーズに分割することができました。

以前は、スタイルの再計算中に ComputedStyle が最終的な値を取得しないことがありました。ComputedStyle が後のパイプライン フェーズで更新された状況がいくつかありました。Project Squad はこれらのコードパスのリファクタリングに成功し、スタイル フェーズの後に ComputedStyle が変更されないようにしました。

LayoutNG: レイアウト フェーズのパイプライン化

この壮大なプロジェクトは RenderingNG の基礎の一つであり、レイアウトのレンダリング フェーズを完全に書き直しました。ここではプロジェクト全体について説明しませんが、BlinkNG プロジェクト全体には注目すべき点がいくつかあります。

  • 以前は、レイアウト フェーズはスタイル フェーズによって作成された LayoutObject のツリーを受け取り、サイズと位置情報のアノテーションをツリーに付けていました。そのため、入力と出力が明確に分離されていませんでした。LayoutNG が導入したフラグメント ツリーは、レイアウトの読み取り専用のプライマリ出力で、後続のレンダリング フェーズへのプライマリ入力として機能します。
  • LayoutNG は、包含プロパティをレイアウトに導入しました。指定された LayoutObject のサイズと位置を計算する際、そのオブジェクトをルートとするサブツリーの外側を見ることはなくなりました。特定のオブジェクトのレイアウトを更新するために必要な情報はすべて事前に計算され、読み取り専用の入力としてアルゴリズムに渡されます。
  • 以前は、レイアウト アルゴリズムが厳密には機能しないエッジケースがありました。アルゴリズムの結果は、直近のレイアウト更新に依存していました。LayoutNG により、そのようなケースがなくなりました。

プリペイント フェーズ

以前は、正式なプリペイント レンダリング フェーズはなく、レイアウト後の操作が大量に溜まっていました。プリペイント フェーズは、レイアウト完了後のレイアウト ツリーの体系的な走査として最適に実装できる関連関数がいくつかあるという認識から生まれたものです。最も重要なのは

  • ペイント無効化の発行: 情報が不完全な場合、レイアウト中にペイント無効化を正しく行うことは非常に困難です。2 つの異なるプロセスに分割すると、わかりやすくなり、非常に効率的になります。スタイルとレイアウトの際、単純なブール値のフラグを使用してコンテンツを「おそらくペイント無効化が必要」とマークできます。プリペイント ツリー ウォーク中に、これらのフラグをチェックし、必要に応じて無効化を発行します。
  • ペイント プロパティ ツリーの生成: 後で詳細に説明するプロセスです。
  • ピクセル スナップされたペイント位置の計算と記録: 記録された結果は、冗長な計算を行うことなく、ペイント フェーズだけでなく、それを必要とするダウンストリーム コードでも使用できるようになります。

プロパティ ツリー: 一貫したジオメトリ

プロパティ ツリーは、スクロールの複雑さに対処するために RenderingNG の初期に導入されました。これはウェブでは他の種類の視覚効果とは構造が異なります。プロパティ ツリーが登場する前は、Chromium のコンポジタは単一の「レイヤ」を使用していましたして、合成コンテンツの幾何学的関係を表すように階層化していましたが、position:fixed のような機能の完全な複雑さが明らかになったため、すぐに崩壊しました。レイヤ階層で、「スクロールの親」を示すローカル以外の余分なポインタが増えたまたは「親クリップ」すぐに理解しづらくなってしまいました。

プロパティ ツリーは、コンテンツのオーバーフロー スクロールやクリップの側面を他の視覚効果とは別に表現することで、この問題を解消しました。これにより、ウェブサイトの実際の視覚的構造とスクロール構造を正しくモデル化できるようになりました。次は「すべて」です私たちがしなければならなかったのは、合成レイヤの画面空間変換などのプロパティ ツリーの上にアルゴリズムを実装することや、スクロールするレイヤとスクロールしないレイヤを特定することでした。

実際、同じような幾何学的な疑問が提起されている箇所がコード内に多数あることにすぐに気づきました。(主なデータ構造の投稿で、より詳細なリストをご覧いただけます)。そのうちのいくつかは、コンポジタのコードと同じ内容の実装が重複していました。それぞれバグのサブセットが異なり実際のウェブサイトの構造を適切にモデル化したものはありませんでした。そして、すべてのジオメトリ アルゴリズムを 1 か所に集約し、そのアルゴリズムを使用するようにすべてのコードをリファクタリングするという解決策が明らかになりました。

これらのアルゴリズムはすべてプロパティ ツリーに依存しているため、プロパティ ツリーは RenderingNG のキーデータ構造、つまりパイプライン全体で使用されるデータ構造です。そのため、ジオメトリ コードの一元化というこの目標を達成するには、パイプラインのかなり早い段階で(プリペイントで)プロパティ ツリーのコンセプトを導入し、現在依存しているすべての API を変更して、実行前にプリペイントを実行できるようにする必要がありました。

このストーリーは、BlinkNG リファクタリング パターンのもう一つの側面です。重要な計算を特定し、重複を避けるためにリファクタリングし、明確に定義されたパイプライン ステージを作成して、計算にフィードするデータ構造を作成します。プロパティ ツリーは、必要な情報がすべて揃った時点で計算されます。また、後のレンダリング ステージの実行中にプロパティ ツリーが変化することはありません。

ペイント後の合成: ペイントのパイプライン化と合成

レイヤ化とは、どの DOM コンテンツが独自の合成レイヤ(つまり GPU テクスチャを表すレイヤ)に入るかを決定するプロセスです。RenderingNG が登場する前は、レイヤー化はペイントの後ではなく、ペイントの前に実行されていました(現在のパイプラインについては、こちらをご覧ください。順序の変更に注意してください)。まず DOM のどの部分が合成レイヤに入るかを決定し、次にそのテクスチャのディスプレイ リストを描画します。当然ながら、決定は、アニメーション化またはスクロールしている DOM 要素、3D 変換が行われる DOM 要素、その上に描画される要素などの要因に依存していました。

このことが大きな問題を引き起こしました。レンダリング パイプラインにとって大きな問題となる、循環依存関係がコード内に存在することが多少なりとも必要だったからです。例を挙げて理由を見ていきましょう。ペイントを無効にする必要があるとします(つまり、ディスプレイ リストを再描画してから、もう一度ラスターで描画する必要があるとします)。無効化が必要になるのは、DOM の変更、またはスタイルやレイアウトの変更によるものです。もちろん、無効にしたいのは実際に変更された部分のみです。つまり、影響を受けた合成レイヤを特定し、そのレイヤのディスプレイ リストの一部またはすべてを無効にしていました。

つまり、無効化は、DOM、スタイル、レイアウト、過去のレイヤ化の決定(過去: 前にレンダリングされたフレームの意味)に依存していました。しかし、現在のレイヤ化は、これらすべてに依存します。すべてのレイヤ化データのコピーが 2 つなかったため、過去と将来のレイヤ化の決定の違いを見分けることが困難でした。その結果、循環推論を使用したコードが大量に発生しました。そのため、非論理的なコードや誤ったコード、さらにはクラッシュやセキュリティの問題を引き起こすことがありました。

この状況に対処するため、早い段階で DisableCompositingQueryAsserts オブジェクトのコンセプトを導入しました。多くの場合、コードが過去のレイヤ化の決定をクエリしようとすると、アサーションが失敗し、デバッグモードになっているブラウザはクラッシュします。これにより、新たなバグの発生を防ぐことができます。また、過去の階層化の決定を照会するためにコードが正当に必要だった各ケースでは、DisableCompositingQueryAsserts オブジェクトを割り当てることで、それを可能にするコードを挿入しました。

そこで、時間をかけてすべてのコールサイトの DisableCompositingQueryAssert オブジェクトを削除し、コードを安全かつ正確であることを宣言することにしました。しかし、ペイント前にレイヤ化が行われている限り、多くの呼び出しを削除することは本質的に不可能であることが判明しました。(ようやく削除できたのは、つい最近のことです)。これが Composite After Paint プロジェクトで発見された最初の理由です。学んだのは、オペレーションのパイプライン フェーズが明確に定義されていても、パイプライン内の間違った場所に配置されていると、いずれ行き詰まるということです。

Composite After Paint プロジェクトが起きた 2 つ目の理由は、Fundamental Compositing のバグです。一つの例として、DOM 要素は、ウェブページ コンテンツの効率的または完全なレイヤ化スキームを 1 対 1 で適切に表現したものではありません。また、合成はペイントの前だったため、多かれ少なかれ本質的に DOM 要素に依存しており、表示リストやプロパティ ツリーには依存していません。これは、プロパティ ツリーを導入した理由と非常によく似ています。プロパティ ツリーと同様に、適切なパイプライン フェーズを見つけ、適切なタイミングで実行し、正しい主要なデータ構造を提供すれば、ソリューションは直接機能します。プロパティ ツリーと同様に、これはペイント フェーズが完了すると、後続のすべてのパイプライン フェーズで出力が不変であることを保証する良い機会となりました。

利点

これまで見てきたように、レンダリング パイプラインを明確に定義することは、長期的に多大なメリットをもたらします。他にも次のようなことがありました。

  • 信頼性の大幅な向上: これは非常にシンプルです。明確に定義され、理解しやすいインターフェースを備えた簡潔なコードにより、理解、記述、テストが容易になります。これにより信頼性が向上します。また、コードの安全性と安定性が向上し、クラッシュや解放後のメモリ使用のバグが減少します。
  • テスト カバレッジの拡大: BlinkNG の過程で、多数の新しいテストがスイートに追加されました。これには、内部構造に焦点を当てた検証を行う単体テストが含まれます。修正した古いバグ(多数のバグ)を再導入するのを防ぐ回帰テストウェブ標準への準拠を測定するために、すべてのブラウザが使用する Web Platform テストスイートに多くの追加機能が追加されています。
  • 拡張が簡単: システムが明確なコンポーネントに分割されている場合、現在のコンポーネントを進めるために他のコンポーネントを詳しく理解する必要はありません。これにより、熟練したエキスパートでなくても、誰でも簡単にレンダリング コードに価値を付加できます。また、システム全体の動作の推論も容易になります。
  • パフォーマンス: スパゲッティ コードで記述されたアルゴリズムの最適化は非常に困難ですが、ユニバーサル スレッドのスクロールとアニメーションや、このようなパイプラインなしでサイト分離のためのプロセスとスレッドなど、さらに大きなことを達成することはほぼ不可能です。並列処理はパフォーマンスを大幅に改善するのに役立ちますが、非常に複雑でもあります。
  • 収束と封じ込め: BlinkNG によって可能になったいくつかの新機能があり、パイプラインを斬新な方法で実行します。たとえば、予算が期限切れになるまでレンダリング パイプラインのみを実行したい場合はどうすればよいでしょうか。あるいは、現時点でユーザーに関連性がないことがわかっているサブツリーのレンダリングをスキップしますか?これを実現するのが、CSS プロパティ content-visibility によって実現されます。コンポーネントのスタイルをレイアウトに依存させるのはどうでしょうか。これがコンテナクエリです。

ケーススタディ: コンテナクエリ

コンテナクエリは、待望の今後のウェブ プラットフォーム機能です(ここ数年、CSS デベロッパーから最も要望が多かった機能です)。素晴らしいのなら、なぜまだ存在しないのでしょう?その理由は、コンテナクエリを実装する際は、スタイル コードとレイアウト コードの関係を入念に理解して管理する必要があるからです。では 詳しく見ていきましょう

コンテナクエリを使用すると、要素に適用されるスタイルを、祖先の配置サイズに依存させることができます。レイアウトのサイズはレイアウト中に計算されるため、レイアウト後にスタイルを再計算する必要があります。ただし、スタイルの再計算はレイアウトの前に実行されます。このニワトリと卵のパラドックスが、BlinkNG 以前のコンテナクエリを実装できなかった完全な理由です。

どうすれば解決できますか?パイプラインへの逆依存関係、つまり Composite After Paint などのプロジェクトが解決した問題と同じ問題ではありませんか?さらに悪いことに、新しいスタイルによって祖先のサイズが変更された場合はどうなるでしょうか。それで無限ループに陥ることもあるんじゃないの?

原則として循環依存関係は、contains CSS プロパティを使用して解決できます。これにより、要素外でのレンダリングが、その要素のサブツリー内のレンダリングに依存しないで可能になります。つまり、コンテナによって適用される新しいスタイルがコンテナのサイズに影響を与えることはありません。コンテナクエリには包含が必要なためです。

しかし実際には、それだけでは不十分で、単にサイズの封じ込めよりも弱いタイプの封じ込めを採用する必要がありました。これは、コンテナクエリ コンテナでは、インライン ディメンションに基づいて一方向(通常はブロック)にのみサイズ変更を許可することが一般的であるためです。そのため、インライン サイズの封じ込めというコンセプトが追加されました。しかし、そのセクションの非常に長い説明からわかるように、インライン サイズの包含が可能かどうかについては、長い間はまったく明確ではありませんでした。

コンテインメントを抽象仕様言語で記述することと、それを正しく実装することは、まったく別のことです。BlinkNG の目標の一つは、レンダリングのメイン ロジックを構成するツリーウォークに封じ込めの原則を導入することでした。つまり、サブツリーを走査する際にサブツリーの外部から情報を要求しないようにすることでした。レンダリング コードが封じ込めの原則に従っている場合、これは偶発的なものではありません

今後: メインスレッド以外での合成

こちらに示したレンダリング パイプラインは、実際には現在の RenderingNG 実装よりも少し進んでいます。レイヤ化がメインスレッドから外れていることが示されていますが、現在はメインスレッドにあります。ただし、これで完了まで時間の問題です。Composite After Paint が出荷され、レイヤ化がペイント後になりました。

これがなぜ重要なのか、そしてどこへ向かうのかを理解するには、レンダリング エンジンのアーキテクチャを、やや高い視点から検討する必要があります。Chromium のパフォーマンス改善に対する最も永続的な障害の 1 つは、レンダラのメインスレッドが、メインのアプリケーション ロジック(つまり、スクリプトの実行)とレンダリングの大部分の両方を処理するという単純な事実です。その結果、メインスレッドが処理で飽和状態になることが多く、メインスレッドの輻輳がブラウザ全体のボトルネックになることがよくあります。

こんな風にする必要はありません。Chromium のアーキテクチャのこの側面は、シングル スレッドの実行が主要なプログラミング モデルだった KHTML 時代までさかのぼります。消費者向け機器でマルチコア プロセッサが一般的に使用されるようになる頃には、シングルスレッドの前提が Blink(以前の WebKit)に完全に組み込まれました。Google は長い間、レンダリング エンジンにより多くのスレッドを導入したいと考えていましたが、古いシステムではまったく不可能でした。レンダリング NG の主な目的の一つは、この穴を切り抜けて、レンダリング処理の一部または全部を別のスレッド(またはスレッド)に移動できるようにすることでした。

現在、BlinkNG は完成に近づいており、すでにこの領域の調査を始めています。非ブロッキング コミットは、レンダラのスレッドモデルを変更する最初の段階です。コンポジタ commit(または単に commit)は、メインスレッドとコンポジタ スレッド間の同期ステップです。commit の際に、メインスレッドで生成されたレンダリング データのコピーを作成し、コンポジタ スレッドで実行されているダウンストリームの合成コードで使用されます。この同期が行われている間、コードのコピーがコンポジタ スレッドで実行されている間、メインスレッドの実行は停止されます。これは、コンポジタ スレッドがレンダリング データをコピーしている間にメインスレッドがレンダリング データを変更しないようにするために行われます。

非ブロッキング コミットを使用すると、メインスレッドが停止して commit ステージの終了を待つ必要がなくなります。コンポジタ スレッドで commit が同時に実行されている間、メインスレッドは処理を継続します。非ブロッキング コミットの実質的な効果は、メインスレッドでのレンダリング作業に費やす時間が短縮されることです。これにより、メインスレッドの輻輳が減少し、パフォーマンスが向上します。このドキュメントの執筆時点(2022 年 3 月)の時点で、非ブロッキング コミットの実用的なプロトタイプが用意されており、パフォーマンスへの影響を詳細に分析する準備が整っています。

翼では Off-main-thread Compositing(オフメインスレッド コンポジット)が待機しています。これは、レイヤ化をメインスレッドからワーカー スレッドに移動することで、レンダリング エンジンをイラストと一致させることを目的としています。非ブロッキング コミットと同様に、レンダリング ワークロードが軽減され、メインスレッドの輻輳が軽減されます。Composite After Paint のアーキテクチャ改善がなければ、このようなプロジェクトは実現しませんでした。

パイプラインにはさらに多くのプロジェクトがあります。ついにレンダリング処理の再配布のテストを可能にする基盤が整いました。何が実現できるのか、とても楽しみです。