FloatSoda の仕組み(使う人向け)
画面に出るまでの流れ
Section titled “画面に出るまでの流れ”書いたウィジェットは、作り直し、レイアウト、描画の記録の順に処理され、別のスレッドで描かれた絵が、テクスチャとして SteamVR に渡されます。この流れを知っておくと、遅い、描き直されない、スレッドの問題が出た、といった症状から、原因の当たりをつけられます。
次の図は、1フレームで起きる処理を上から下の順に並べたものです。
メインスレッド ウィジェットの作り直し 変わった部分だけ ↓ レイアウト 大きさと位置を決める ↓ ペイント 描く内容を「レイヤー」に記録する ↓ レイヤーを複製して渡すレンダースレッド Skia で描画 レイヤーを絵にする ↓ OpenGL のテクスチャ ↓SteamVR のオーバーレイ内部は、3つのツリー(ウィジェットと Element、RenderObject、レイヤー)で組み立てられています。ウィジェットのツリーが作り直されると、RenderObject のツリーがレイアウトと描画の記録を行い、その結果がレイヤーのツリーになります。使う側が書くのはウィジェットだけで、残りの2つは FloatSoda が動かします。
1フレームの中で、メインスレッドがすること
Section titled “1フレームの中で、メインスレッドがすること”メインスレッドは、毎フレーム、次の4つをこの順に行います。
CreateWindowで予約したウィンドウの作成など、保留中のタスクを実行する- SteamVR のイベントを処理する
- コントローラーの入力を受け取るで登録したアクションの値とイベントを更新する
- 各ウィンドウの描画を準備する(ウィジェットの作り直し、レイアウト、ペイント)
4つを終えたら、次のフレームの時刻まで待ちます。目標のフレームレートは、既定で 60 です。FloatSodaOptions.TargetFrameRate で変えられます(設定やログ、裏で動く処理を足す)。
描画は別のスレッドで行われる
Section titled “描画は別のスレッドで行われる”ここでは、ウィジェットの処理を行うメインスレッドと、描画を行うレンダースレッドを説明します。Build() はメインスレッドで実行されます。SetState() も、メインスレッドから呼びます。レンダースレッドは、Skia による描画と、OpenGL と SteamVR とのやり取りを受け持ちます。
メインスレッドは、ペイントで作ったレイヤーのツリーを複製して、レンダースレッドに渡します。複製して渡すのは、メインスレッドが次のフレームでレイヤーを書き換えているあいだに、レンダースレッドが同じレイヤーを読んで、データがぶつかるのを避けるためです。
自分のコードが動くスレッドは、書く場所で決まります。ウィジェットの Build() とアクションのイベントはメインスレッドで動きます。BackgroundService のような裏の処理は、別のスレッドで動きます。
変わった部分だけが処理される
Section titled “変わった部分だけが処理される”作り直し、レイアウト、ペイントは、毎フレーム全体をやり直すわけではありません。それぞれ更新が必要になった部分だけが処理されます。作り直しの対象は、SetState() などで変更を知らせたウィジェットです。変わった部分がないフレームでは、レイアウトとペイントを行いません。
そのため、SetState() を呼ぶこと自体を避ける必要はありません。値が変わったら呼ぶ、という書き方で足ります。作り直しの考え方は、状態と作り直しにあります。
止まったり終わったりするとき
Section titled “止まったり終わったりするとき”保留中のタスクの実行、イベントの処理、メインスレッドでの描画の準備(作り直し、レイアウト、ペイント)のどれかで例外が出ると、メインループが止まります。そのとき、タスクエラー、イベント処理エラー、描画エラー のいずれかで始まるメッセージが、エラーのログに出ます。メインループが止まると、FloatSoda のランタイムも終了処理に入り、アプリが終わります。
SteamVR が終了を通知したときも、アプリは終わります。SteamVR を閉じると、FloatSoda のアプリも一緒に終了します。
アプリが止まった、または画面が出ないときは、エラーを読んで直すで、ログのメッセージから原因を切り分けます。
もっと深く知りたいとき
Section titled “もっと深く知りたいとき”各ツリーの内部と、差分更新の詳しい動きは、コントリビューター向けの解説にあります。1フレームが SteamVR に届くまでの流れは1フレームが SteamVR に届くまで、ウィジェットのツリーはフレームワーク層で読めます。