Blink は、Chromium によるウェブ プラットフォームの実装を指し、コンポジット前のレンダリングのすべてのフェーズを網羅し、最終的にはコンポジタ コミットに至ります。Blink レンダリング アーキテクチャについて詳しくは、このシリーズの以前の記事をご覧ください。
Blink は WebKit のフォークとして始まりました。WebKit 自体は 1998 年に登場した KHTML のフォークです。ここに含まれるコードは Chromium の中でも最も古く(かつ最も重要な)ものであり、2014 年までにその古さは明らかでした。その年、Google は BlinkNG という旗印の下、野心的なプロジェクトに着手しました。これは、Blink コードの組織と構造の長年の欠陥に対処することを目的としています。この記事では、BlinkNG とその構成プロジェクトについて、その理由、達成したこと、設計を形作ったガイドライン、今後の改善の機会について説明します。
レンダリング(NG 前)
Blink 内のレンダリング パイプラインは、概念的には常にフェーズ(スタイル、レイアウト、ペイントなど)に分割されていましたが、抽象化の障壁は漏れていました。一般的に、レンダリングに関連するデータは、長期間存続する変更可能なオブジェクトで構成されていました。これらのオブジェクトはいつでも変更可能で、実際に変更されていました。また、連続したレンダリングの更新によって頻繁にリサイクルされ、再利用されていました。次のような簡単な質問に確実に答えることはできませんでした。
- スタイル、レイアウト、ペイントの出力を更新する必要があるか
- これらのデータの「最終的な」値はいつ得られますか?
- これらのデータを変更できるタイミング
- このオブジェクトはいつ削除されますか?
たとえば、次のような例があります。
スタイルは、スタイルシートに基づいて ComputedStyle
を生成しますが、ComputedStyle
は不変ではありません。後続のパイプライン ステージで変更される場合があります。
スタイルは LayoutObject
のツリーを生成し、レイアウトはこれらのオブジェクトにサイズと配置情報をアノテーションします。場合によっては、レイアウトによってツリー構造が変更されることもあります。レイアウトの入力と出力の区別が明確ではありませんでした。
スタイルでは、合成の流れを決定するアクセサリ データ構造が生成され、これらのデータ構造はスタイル後の各フェーズでその場で変更されました。
低レベルでは、レンダリング データ型は主に特殊なツリー(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 フェーズが完了するまで最終的なスタイル プロパティ値を決定しなかった特殊なケースがいくつかありました。レンダリング プロセスにおいて、スタイル情報が完全かつ不変であると確実に言える正式なポイントや適用可能なポイントはありませんでした。
BlinkNG 以前の問題の良い例として、ペイントの無効化があります。以前は、ペイントの無効化がペイントに至るすべてのレンダリング フェーズに散在していました。スタイルコードやレイアウトコードを変更する際に、ペイント無効化ロジックにどのような変更が必要かを把握することが難しく、誤って無効化が不足または過剰になるバグが発生しやすかった。古いペイント無効化システムの詳細については、このシリーズの LayoutNG に関する記事をご覧ください。
ペイントのためにサブピクセル レイアウト ジオメトリをピクセル境界全体にスナップする機能は、同じ機能が複数実装され、重複する作業が多かった例です。ペイント システムで使用されるピクセル スナップ コードパスが 1 つあり、ペイント コードの外部でピクセル スナップされた座標の 1 回限りのオンザフライ計算が必要な場合は、まったく別のコードパスが使用されていました。言うまでもなく、各実装には独自のバグがあり、結果が一致するとは限りませんでした。この情報はキャッシュに保存されなかったため、システムがまったく同じ計算を繰り返し実行することがあり、パフォーマンスに負荷がかかっていました。
ペイント前のレンダリング フェーズのアーキテクチャ上の欠陥を解消した重要なプロジェクトをいくつかご紹介します。
Project Squad: スタイルフェーズのパイプライン化
このプロジェクトでは、スタイルフェーズでパイプラインをきれいにできない 2 つの主な欠陥に対処しました。
スタイルフェーズには、ComputedStyle
と LayoutObjects
の 2 つの主な出力があります。ComputedStyle
には、DOM ツリーに対して CSS カスケード アルゴリズムを実行した結果が含まれます。LayoutObjects
のツリーは、レイアウト フェーズのオペレーションの順序を設定します。概念的には、カスケード アルゴリズムの実行は、厳密にはレイアウト ツリーの生成の前に行われるべきですが、以前は、これらの 2 つのオペレーションが交互に行われていました。Project Squad では、これらを明確に区別された連続的なフェーズに分割することに成功しました。
以前は、スタイルの再計算中に ComputedStyle
が最終的な値を取得するとは限りませんでした。パイプラインの後半のフェーズで ComputedStyle
が更新される場合もありました。Project Squad はこれらのコードパスを正常にリファクタリングし、スタイルフェーズ後に ComputedStyle
が変更されないようにしました。
LayoutNG: レイアウト フェーズのパイプライン化
この大規模なプロジェクトは、RenderingNG の基盤となるプロジェクトの 1 つで、レイアウト レンダリング フェーズを完全に書き換えました。ここではプロジェクト全体について説明しません。ただし、BlinkNG プロジェクト全体には、注目すべき点がいくつかあります。
- 以前は、レイアウト フェーズはスタイル フェーズによって作成された
LayoutObject
のツリーを受け取り、サイズと位置情報をツリーにアノテーションを付けていました。そのため、入力と出力が明確に分離されていませんでした。LayoutNG では、フラグメント ツリーが導入されました。これはレイアウトのプライマリな読み取り専用出力であり、後続のレンダリング フェーズへのプライマリ入力として機能します。 - LayoutNG では、制限プロパティがレイアウトに導入されました。特定の
LayoutObject
のサイズと位置を計算する際に、そのオブジェクトをルートとするサブツリー外部を参照しなくなりました。特定のオブジェクトのレイアウトを更新するために必要な情報はすべて事前に計算され、アルゴリズムに読み取り専用入力として提供されます。 - 以前は、レイアウト アルゴリズムが厳密に機能しないエッジケースがありました。アルゴリズムの結果は、直近のレイアウト更新に依存していました。LayoutNG では、このようなケースは排除されています。
塗装前のフェーズ
以前は、ペイント前の正式なレンダリング フェーズはなく、レイアウト後のさまざまなオペレーションが混在していました。ペイント前フェーズは、レイアウトが完了した後にレイアウト ツリーを体系的に走査することで実装するのが最適な関連機能がいくつかあるという認識から生まれました。最も重要な機能は次のとおりです。
- ペイントの無効化の発行: 不完全な情報があるレイアウトの過程で、ペイントの無効化を正しく行うことは非常に困難です。2 つの異なるプロセスに分割すると、正しく行うのがはるかに簡単で、非常に効率的です。スタイルとレイアウト中に、コンテンツを「ペイントの無効化が必要になる可能性がある」という単純なブール値フラグでマークできます。ペイント前のツリーウォーク中に、これらのフラグがチェックされ、必要に応じて無効化が実行されます。
- ペイント プロパティ ツリーの生成: このプロセスについては後で詳しく説明します。
- ピクセル スナップされたペイント位置の計算と記録: 記録された結果は、ペイント フェーズで使用できるほか、冗長な計算を行うことなく、それらを必要とするダウンストリーム コードでも使用できます。
プロパティ ツリー: 一貫したジオメトリ
プロパティ ツリーは、RenderingNG の初期段階で、スクロールの複雑さを処理するために導入されました。ウェブでは、他の種類の視覚効果とは異なる構造になっています。プロパティ ツリーが導入される前は、Chromium のコンポーザは、合成されたコンテンツの幾何学的関係を表現するために単一の「レイヤ」階層を使用していましたが、position:fixed などの機能の複雑さが明らかになったため、すぐに破綻しました。レイヤ階層に、レイヤの「スクロール ペアレント」または「クリップ ペアレント」を示すローカル以外のポインタが追加され、すぐにコードを理解するのが非常に難しくなりました。
プロパティ ツリーでは、コンテンツのオーバーフロー スクロールとクリップ アスペクトを他のすべての視覚効果とは別に表現することで、この問題を解決しました。これにより、ウェブサイトの実際の視覚構造とスクロール構造を正確にモデル化できるようになりました。次に、合成レイヤのスクリーン空間変換や、スクロールされるレイヤとスクロールされないレイヤの決定など、プロパティ ツリーの上にアルゴリズムを実装するだけでした。
実際、コードの他の多くの場所で、同様の幾何学的な問題が提起されていることがすぐにわかりました。(主要なデータ構造に関する投稿に、より完全なリストがあります)。いくつかのコードでは、コンポジタ コードが実行していたものと同じ処理が重複して実装されていました。また、バグの種類はすべて異なり、どのコードもウェブサイトの実際の構造を適切にモデル化していませんでした。解決策は明らかでした。すべてのジオメトリ アルゴリズムを 1 か所に集約し、それを使用するようにすべてのコードをリファクタリングします。
これらのアルゴリズムはすべてプロパティ ツリーに依存しているため、プロパティ ツリーは RenderingNG のキー データ構造(パイプライン全体で使用されるデータ構造)です。そのため、ジオメトリ コードを統合するという目標を達成するには、パイプラインのはるか前段階(プリペイント)でプロパティ ツリーのコンセプトを導入し、それらに依存するすべての API を変更して、実行前にプリペイントを実行するようにする必要がありました。
このストーリーは、BlinkNG リファクタリング パターンの別の側面です。主要な計算を特定し、重複を避けるためにリファクタリングし、それらを供給するデータ構造を作成する明確なパイプライン ステージを作成します。プロパティ ツリーは、必要な情報がすべて利用可能になった時点で計算されます。また、後続のレンダリング ステージの実行中にプロパティ ツリーが変更されないようにします。
ペイント後の合成: ペイントと合成のパイプライニング
レイヤ化とは、どの DOM コンテンツを独自の合成レイヤ(GPU テクスチャを表す)に含めるかを判断するプロセスです。RenderingNG より前は、レイヤ化はペイントの後ではなく、ペイントの前に実行されていました(現在のパイプラインについてはこちらをご覧ください。順序の変更にご注意ください)。まず、DOM のどの部分をどの合成レイヤに配置するかを決定し、それからそのテクスチャのディスプレイリストを描画します。当然ながら、どの DOM 要素がアニメーション化またはスクロールされているか、3D 変換が適用されているか、どの要素がどの要素の上に描画されているかなどの要素によって、決定は異なりました。
これは大きな問題を引き起こしました。コードに循環依存関係が必要になることがほぼ確実だったためです。これはレンダリング パイプラインにとって大きな問題です。例でその理由を確認しましょう。ペイントを無効にする必要があるとします(つまり、表示リストを再描画してから再度ラスタ化する必要があります)。無効化の必要性は、DOM の変更、スタイルやレイアウトの変更によって生じる可能性があります。ただし、実際に変更された部分のみを無効にする必要があります。つまり、影響を受けた合成レイヤを特定し、それらのレイヤのディスプレイ リストの一部またはすべてを無効にする必要がありました。
つまり、無効化は DOM、スタイル、レイアウト、過去のレイヤ化の決定(過去: 以前にレンダリングされたフレーム)に依存していました。ただし、現在のレイヤ化は、それらすべてに依存しています。また、すべてのレイヤ化データのコピーが 2 つないため、過去と将来のレイヤ化の決定の違いを判断するのが困難でした。そのため、循環論法を含むコードが大量に作成されました。そのため、不合理なコードや誤ったコードが作成されたり、十分な注意を払わなかった場合はクラッシュやセキュリティの問題が発生したりすることがありました。
この状況に対処するため、早い段階で DisableCompositingQueryAsserts
オブジェクトのコンセプトを導入しました。ほとんどの場合、コードが過去のレイヤ化の決定をクエリしようとすると、アサーションの失敗が発生し、デバッグ モードの場合はブラウザがクラッシュします。これにより、新しいバグの導入を回避できました。過去のレイヤ化の決定をコードで正当にクエリする必要がある場合は、DisableCompositingQueryAsserts
オブジェクトを割り当てて、それを許可するコードを追加しました。
最終的には、すべての呼び出しサイトの DisableCompositingQueryAssert
オブジェクトを削除し、コードが安全で正しいことを宣言する予定でした。しかし、ペイントの前にレイヤ化が行われている限り、多くの呼び出しを削除することは基本的に不可能であることがわかりました。(つい最近ようやく削除できました)。これは、Composite After Paint プロジェクトで最初に発見された原因です。オペレーションのパイプライン フェーズが明確に定義されている場合でも、パイプラインの間違った場所にあると、最終的には停止することがわかりました。
2 つ目の理由は、基本的な合成のバグです。このバグを説明する方法の一つは、DOM 要素はウェブページ コンテンツの効率的なレイヤリング スキームや完全なレイヤリング スキームを 1 対 1 で表すものではないということです。合成はペイントの前に行われるため、ディスプレイ リストやプロパティ ツリーではなく、DOM 要素に依存していました。これは、プロパティ ツリーを導入した理由と非常によく似ています。プロパティ ツリーと同様に、適切なパイプライン フェーズを特定し、適切なタイミングで実行し、適切なキーデータ構造を指定すると、ソリューションが直接得られます。また、プロパティ ツリーと同様に、ペイント フェーズが完了すると、その後のすべてのパイプライン フェーズで出力が変更されないことを保証する良い機会でした。
利点
ご覧のとおり、明確に定義されたレンダリング パイプラインは、長期的に大きなメリットをもたらします。想像以上に多くの機能があります。
- 信頼性が大幅に向上: これは非常に簡単です。明確でわかりやすいインターフェースを持つクリーンなコードは、理解、作成、テストが容易です。これにより信頼性が高まります。また、コードの安全性と安定性が向上し、クラッシュや解放後の使用バグが減少します。
- テスト範囲の拡大: BlinkNG の過程で、スイートに多くの新しいテストが追加されました。これには、内部の集中的な検証を行う単体テスト、修正済みの古いバグを再導入しないようにする回帰テスト、公開されている共同メンテナンスのウェブ プラットフォーム テスト スイートへの多くの追加が含まれます。このスイートは、すべてのブラウザがウェブ標準への準拠性を測定するために使用しています。
- 拡張が容易: システムが明確なコンポーネントに分割されている場合、現在のコンポーネントを進展させるために、他のコンポーネントを詳細に理解する必要はありません。これにより、専門知識がなくても誰でもレンダリング コードに価値を追加しやすくなり、システム全体の動作を簡単に把握できるようになります。
- パフォーマンス: スパゲッティ コードで記述されたアルゴリズムを最適化するのは難しいですが、このようなパイプラインなしでユニバーサル スレッド スクロールとアニメーション、サイト分離のプロセスとスレッドなどのさらに大きなことを実現するのはほぼ不可能です。並列処理はパフォーマンスを大幅に向上させることができますが、非常に複雑です。
- イージングと制限: BlinkNG によって可能になった新機能がいくつかあり、パイプラインを新しい方法で実行できます。たとえば、予算が期限切れになるまでレンダリング パイプラインのみを実行したい場合はどうすればよいでしょうか。または、現在ユーザーに関連性がないとわかっているサブツリーのレンダリングをスキップしますか?これが、CSS プロパティ content-visibility が実現するものです。コンポーネントのスタイルをレイアウトに依存させるにはどうすればよいですか?それがコンテナクエリです。
ケーススタディ: コンテナクエリ
コンテナクエリは、今後のウェブ プラットフォームの機能として非常に期待されています(CSS デベロッパーから長年にわたって最も要望の多かった機能です)。そんなに優れているのに、まだ存在しないのはなぜですか?これは、コンテナ クエリの実装では、スタイル コードとレイアウト コードの関係を非常に慎重に理解し、制御する必要があるためです。では 詳しく見ていきましょう
コンテナクエリを使用すると、要素に適用されるスタイルを、祖先のレイアウトサイズに依存させることができます。レイアウト時のサイズはレイアウト中に計算されるため、レイアウト後にスタイルの再計算を実行する必要があります。しかし、スタイルの再計算はレイアウトの前に実行されます。この鶏と卵のパラドックスが、BlinkNG より前にコンテナクエリを実装できなかった理由です。
この問題を解決するにはどうすればよいですか?これは、Composite After Paint などのプロジェクトが解決した、後方パイプラインの依存関係、つまり同じ問題ではないでしょうか?さらに悪いことに、新しいスタイルで祖先のサイズが変更された場合はどうなりますか?無限ループに陥ることはないでしょうか?
原則として、循環依存関係は CSS プロパティ contain を使用して解決できます。これにより、要素の外部のレンダリングがその要素のサブツリー内のレンダリングに依存しないようにできます。つまり、コンテナ クエリには制限が必要であるため、コンテナによって適用される新しいスタイルはコンテナのサイズに影響しません。
しかし、実際には、それだけでは不十分であり、サイズ制限よりも緩いタイプの制限を導入する必要がありました。これは、コンテナ クエリ コンテナがインライン ディメンションに基づいて 1 方向(通常はブロック)のみでサイズ変更できるようにすることが一般的であるためです。そこで、インラインサイズの制限のコンセプトが追加されました。しかし、そのセクションの非常に長い注記からわかるように、インライン サイズの制限が可能かどうかは長い間明確ではありませんでした。
抽象的な仕様言語で制限を記述することは簡単ですが、それを正しく実装するのは別の話です。BlinkNG の目標の 1 つは、レンダリングのメインロジックを構成するツリーウォークに制限原則を適用することでした。サブツリーを走査するときに、サブツリー外部から情報を取得する必要はありません。レンダリング コードが制限の原則に従っている場合、CSS 制限を実装するのは、はるかにクリーンかつ簡単です。
今後: メインスレッド外のコンポジット処理とそれ以降
こちらに示すレンダリング パイプラインは、実際は現在の RenderingNG 実装よりも少し先行しています。レイヤ化はメインスレッド外と表示されていますが、現在はメインスレッド内にあります。ただし、Composite After Paint がリリースされ、レイヤ化がペイント後に行われるようになったため、この問題は時間の問題で解決するでしょう。
これがなぜ重要なのか、また、他にどのような影響を与えるのかを理解するには、レンダリング エンジンのアーキテクチャをより高い視点から検討する必要があります。Chromium のパフォーマンスを向上させる上で最も大きな障害のひとつは、レンダラのメインスレッドがメイン アプリケーション ロジック(スクリプトの実行)とレンダリングの大半の両方を処理するという単純な事実です。その結果、メインスレッドは頻繁に作業で飽和状態になり、メインスレッドの輻輳がブラウザ全体のボトルネックになることがあります。
幸い、そうする必要はありません。Chromium のアーキテクチャのこの側面は、シングルスレッド実行が主流のプログラミング モデルだった KHTML 時代まで遡ります。コンシューマ グレードのデバイスでマルチコア プロセッサが一般的になる頃には、シングルスレッドの前提が Blink(旧 WebKit)に完全に組み込まれていました。レンダリング エンジンにスレッドを追加したいという要望は長い間ありましたが、古いシステムでは不可能でした。レンダリング NG の主な目的の 1 つは、この問題を解決し、レンダリング処理の一部またはすべてを別のスレッド(またはスレッド)に移動できるようにすることでした。
BlinkNG の完成が近づいてきたため、Google はすでにこの分野の調査を開始しています。非ブロッキング commit は、レンダラ スレッドモデルの変更への最初の試みです。コンポジタ commit(または単に commit)は、メインスレッドとコンポジタ スレッド間の同期ステップです。コミット中に、コンポジタ スレッドで生成されたレンダリング データのコピーを作成し、コンポジタ スレッドで実行されるダウンストリーム コンポジット コードで使用できるようにします。この同期が行われている間、コピー コードがコンポジタ スレッドで実行されている間、メインスレッドの実行は停止されます。これは、コンポジタ スレッドがレンダリング データをコピーしている間に、メインスレッドがレンダリング データを変更しないようにするためです。
ノンブロッキング commit を使用すると、メインスレッドを停止して commit ステージの終了を待つ必要がなくなります。メインスレッドは、コンポジタ スレッドで commit が同時に実行されている間、処理を続行します。ノンブロッキング commit の最終的な効果は、メインスレッドのレンダリング処理に費やす時間が短縮されることです。これにより、メインスレッドの輻輳が軽減され、パフォーマンスが向上します。執筆時点(2022 年 3 月)では、ノンブロッキング コミットの動作するプロトタイプが完成しており、パフォーマンスへの影響を詳細に分析する準備を進めています。
メインスレッド外のコンポジット処理も開発中です。この機能は、レイヤ化をメインスレッドからワーカー スレッドに移動することで、レンダリング エンジンをイラストに合わせることを目的としています。非ブロッキング commit と同様に、レンダリング ワークロードを減らすことで、メインスレッドの輻輳を軽減します。このようなプロジェクトは、Composite After Paint のアーキテクチャの改善なしには実現できなかったでしょう。
今後も、さらに多くのプロジェクトがパイプライン(ダジャレです)に追加される予定です。レンダリング作業の再配布をテストできる基盤がようやく整いました。今後の可能性に期待しています。