RenderingNG の詳細: BlinkNG

Stefan Zager 氏
Stefan Zager
クリス・ハレルソン
Chris Harrelson

Blink とは、Chromium のウェブ プラットフォームの実装のことであり、コンポジション前のレンダリングのすべてのフェーズを含み、最終的にはコンポジタ commit になります。ブリンク レンダリング アーキテクチャについては、このシリーズの以前の記事で詳しく解説しています。

BlinkWebKit のフォークとして始まりました。WebKit 自体は 1998 年にまでさかのぼる KHTML のフォークです。これには Chromium で最も古い(かつ最も重要な)コードが含まれ、2014 年までには明らかにその時代遅れになっていました。この年、私たちは BlinkNG と名付けた、一連の野心的なプロジェクトに着手しました。その目的は、Blink コードの構造と構造における長年の欠陥に対処することです。この記事では、BlinkNG とその構成要素プロジェクトについて、その目的、成果、設計を形成した指針、BlinkNG がもたらす改善の機会を探ります。

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

NG 前のレンダリング

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

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

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

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

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

スタイルは、合成の過程を決定するアクセサリのデータ構造を生成し、そのデータ構造はスタイル以降の各フェーズで修正されています。

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

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

変更内容

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

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

BlinkNG のサブプロジェクトをすべて網羅したリストでは面倒な読むことになりますが、具体的な結果を以下に示します。

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

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

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

BlinkNG を実装する過程で、こうした不変条件に反するコードパスを体系的に排除し、回帰を防ぐために、コード全体に多くのアサーションを散りばめました。

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

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

レンダリング パイプラインへのエントリ ポイントは、これら 2 つのシナリオに対応する 2 つだけになりました。リエントラントなコードパスは削除またはリファクタリングされ、中間フェーズからパイプラインに入ることはできなくなりました。これにより、レンダリングの更新がいつ、どのように行われるかに関する多くの不明な点がなくなり、システムの動作について推測するのがはるかに容易になりました。

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

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

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

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

pre-BlinkNG の問題のもう一つの好例は、ペイントの無効化です。以前は、ペイントに至るまでのすべてのレンダリング フェーズで、ペイントの無効化が行われていました。スタイルまたはレイアウトのコードを変更する際、ペイント無効化ロジックにどのような変更が必要かがわかりにくく、ミスが起こりやすく、無効化の過少または過剰なバグの原因となっていました。古いペイント無効化システムの詳細は、LayoutNG に関するこのシリーズの記事をご覧ください。

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

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

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

このプロジェクトでは、スタイルフェーズにおける 2 つの主な欠点に取り組み、クリーンにパイプライン化することを妨げていました。

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

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

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

RenderingNG の基盤のひとつであるこの大きなプロジェクトは、レイアウトのレンダリング フェーズを完全に書き換えたものです。ここではプロジェクト全体を正当化しませんが、BlinkNG プロジェクト全体には注目すべき点がいくつかあります。

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

プリペイント フェーズ

以前は、正式なプレペイント レンダリング フェーズはなく、ポスト レイアウト操作のグラブバッグしかありませんでした。pre-Paint フェーズは、レイアウト完了後のレイアウト ツリーの体系的な走査として実装されるのが最適であるいくつかの関連関数があることから始まりました。最も重要なのは、

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

プロパティ ツリー: 一貫性のあるジオメトリ

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

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

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

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

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

塗装後の複合材: 塗料の配管化と合成

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

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

つまり、無効化は DOM、スタイル、レイアウト、過去のレイヤ化の決定(過去: 前のレンダリングされたフレームの意味)に依存しているということです。現在の階層化は、これらすべての要素に左右されます。また、すべてのレイヤ化データのコピーが 2 つあるわけではないため、過去と将来のレイヤ化に関する決定の違いを見分けるのは困難でした。そのため、循環推論を行う多くのコードができました。細心の注意を払っていないと、コードが非論理的であったり正しくなかったり、クラッシュやセキュリティに問題が生じたりすることがありました。

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

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

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

利点

これまで見てきたように、明確に定義されたレンダリング パイプラインは、長期的な大きなメリットをもたらします。想像を絶するようなこともあるでしょう。

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

事例紹介: コンテナクエリ

コンテナクエリは、今後待望のウェブ プラットフォーム機能です(長年にわたり CSS デベロッパーから最も多くの要望が寄せられていた機能です)。素晴らしいのに、まだないのはなぜですか?コンテナクエリを実装するには、スタイルとレイアウト コードの関係を非常に注意深く理解し、制御する必要があるためです。では 詳しく見ていきましょう

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

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

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

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

抽象的な仕様言語でコンテインメントを記述することは、正しく実装することも重要です。BlinkNG の目標の 1 つは、レンダリングの主なロジックであるツリーウォークに封じ込めの原則を導入することでした。つまり、サブツリーを走査するときに、サブツリーの外部からの情報を要求してはいけません。偶然とは言えませんが、レンダリング コードがコンテインメントの原則を遵守していれば、CSS のコンテインメントの実装ははるかに簡潔で容易になります。

今後: メインスレッド外のコンポジット、さらにその先へ

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

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

必ずしもこのようにする必要はありません。Chromium のアーキテクチャは、KHTML の時代にまでさかのぼります。当時、プログラミング モデルはシングル スレッドが主流でした。マルチコア プロセッサが消費者向けデバイスで一般的に使用されるようになる頃には、シングル スレッドの前提が Blink(以前の WebKit)に徹底的に組み込まれました。長い間、レンダリング エンジンにスレッド機能を導入したいと考えていましたが、以前のシステムではまったく不可能でした。Rendering NG の主な目的の一つは、この穴から抜け出し、レンダリング作業の一部または全体を別のスレッド(スレッド)に移動できるようにすることでした。

BlinkNG の完了が近づいているため、この領域についてはすでに検討し始めています。Non-Blocking Commit は、レンダラのスレッドモデルを変更する最初の取り組みです。コンポジタ commit(または単に commit)は、メインスレッドとコンポジタ スレッド間の同期ステップです。commit の際、メインスレッドで生成されたレンダリング データのコピーを作成し、コンポジタ スレッドで実行されているダウンストリーム合成コードで使用できるようにします。この同期中は、コピーするコードがコンポジタ スレッドで実行されている間、メインスレッドの実行が停止します。これは、コンポジタ スレッドがコピーしている間、メインスレッドがそのレンダリング データを変更しないようにするために行われます。

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

ウィング内で待機するのはメインスレッド外のコンポジットです。メインスレッドからワーカー スレッドに移動することで、レンダリング エンジンをイラストに合わせることを目的としています。非ブロッキング commit と同様に、レンダリング ワークロードが軽減され、メインスレッドの輻輳が軽減されます。このようなプロジェクトは、Composite After Paint のアーキテクチャの改善がなければ実現しませんでした。

そして、さらに多くのプロジェクトが進行中です。レンダリング処理の再配布をテストするための基盤がついに完成しました。何ができるかを見るのが楽しみです。