Skip to content

アーキテクチャ概要

両側の所有権

TSX / hooks           →  Mutation Stream  →   Scene / Layout / Paint
(TypeScript Shell)      バイナリ・バッチ      (Rust Core, wasm)

Canvas2D リプレイヤー  ←   DisplayList     ←     Picture

Shell はコンポーネントツリーを、Core は Scene を所有します。両者は可変オブジェクトを共有しません。 境界を越える通信はすべてバージョン管理されたバイナリストリームです。リトルエンディアン、4 バイト境界、 命令化されており、受信側はメモリに触れる前に opcode、長さ、アラインメント、ID、算術を検証します。 不正な入力は部分適用されずアトミックに拒否されます。

この境界は性能最適化ではなく正しさの境界です。バイトが通常このプロジェクト自身のエンコーダから来る としても、デコーダは信頼できない入力として扱い、ファジングで守られています。

デュアルクロック

UI クロック(メインスレッド)とレンダリングクロック(Worker)は独立しています。

  • メインスレッドは入力を集め、コンポーネントツリーを実行し、Mutation フレームをコミットします。
  • Worker はスクロール物理、アニメーション、レイアウト、合成を駆動します。

スクロール中に Shell を呼び出すことはありません。 欠けているデータはプレースホルダーで描き、 後続フレームで補完します。そのためアプリケーションコードがメインスレッドを 200ms ブロックしても、 スクロールとアニメーションは途切れません。この状況は自動的な障害注入テストで守られています。

フォールバックチェーン

能力検出が順に転送経路を選びます。3 段階はいずれも機能的に等価です。

  1. SharedArrayBuffer —— クロスオリジン分離(COOP/COEP)が必要
  2. postMessage —— SAB が使えない場合
  3. メインスレッド Canvas2D —— Worker / OffscreenCanvas が使えない場合
ts
const root = await createHostedCanvasRoot(canvas, {
  transport: { preference: "sab" }, // 希望であり、満たせなければフォールバックします
});
console.log(root.mode); // "sab" | "post-message" | "main-thread"

このサイトの Playground が実例です。GitHub Pages は COOP/COEP ヘッダを送出できないため 公開版は postMessage 経路で動作し、ページ上部の transport バッジがそれを正直に表示します。

無効化モデル

prop の意味論が無効化ドメインを決めます。 呼び出し側が手動でダーティを立てることはなく、 forceUpdate のような抜け道もありません。

各プロパティは単一のスキーマで、レイアウト・描画・ヒット・セマンティクスのどれに影響するかを宣言します。 opacity の変更は再レイアウトを起こしませんが、width の変更は起こします。ダーティビットマップは ドメインごとに保持され、onFrame が各ドメインのダーティノード数を公開します。

この選択は「積極的に最小の無効化 + プロパティテストの安全網」です。差分描画の結果は全体描画と ピクセル単位で一致しなければならず、差分テストは反例を最小の失敗ケースまで縮小します。

Scene の表現

Core 内の Scene は SoA(構造体の配列ではなく配列の構造体)です。

  • ノード ID は世代を含み、スロットを再利用しても古い ID が再び有効になることはありません。
  • コミット後はトポロジ順を保ちます。親は常に子より前に並びます。
  • 構造変更の詰め直しは mutation ごとではなくコミットごとに一度だけ行います。
  • レイアウト結果はダブルバッファの SoA でまとめて比較し、ホットパスでノードごとのクロージャや リスナーを確保しません。

差し替え可能なバックエンド

Core はフラットなバイナリ DisplayList を出力し、バックエンドは単なるリプレイヤーです。Canvas2D バックエンドは確保を切り詰めた typed-array のループです。描画ごとに wasm→JS を呼ぶ設計は 許容できるレンダリング経路ではありません。

同じ DisplayList を隔離された wgpu プロトタイプにも与え、両者の出力をピクセル差分します。 WebGPU を採用するかどうかはデータで決める事項です。ADR-0006 を参照してください。

決定性

時間、乱数源、入力ストリームはすべて注入または再生でき、Core の出力はスレッドのスケジューリング順に 依存しません。DOPR アーカイブは Mutation と Input のストリームを元の順序で記録し、ブラウザなしの ヘッドレス環境で決定的に再生できます。本番の問題を手元で再現できる一方、機微な編集ストリームは 明示的に記録から除外されます。

さらに詳しく

アルゴリズム、データ構造、受け入れ基準の全体は技術設計文書にあります。

MIT ライセンスで公開