Version: 2021.3
言語: 日本語
XR SDK 入力サブシステム
XR SDK メッシュ化サブシステム

XR SDK ディスプレイサブシステム

XR SDK ディスプレイサブシステムは、テクスチャの割り当て、フレームのライフサイクル、cadence のためのブロッキングなどのインターフェースを提供します。

テクスチャの割り当て

デバイス SDK のなかには、通常のグラフィックス API ではなく、SDK 自体でテクスチャを割り当てる必要があるものもあります。XR SDK ディスプレイサブシステムを使用している場合は、SDKテクスチャへの転送やコピーを外部プラグインに依存する必要はありません。

ディスプレイサブシステムを使うと、プラグインプロバイダーがテクスチャを割り当てることができるようになります。可能な限り、Unity は不必要なコピーを避けるためにテクスチャに直接レンダリングします。必要に応じて、Unity はテクスチャを割り当てることもできます。

以下のケースでは、Unity はテクスチャに直接レンダリングすることができず、代わりに中間テクスチャにレンダリングしてから、テクスチャに転送かコピーします。

  • イメージエフェクトが使用されている場合、テクスチャはチェーンにとどまります。
  • MSAA のある PC では、Unity はマルチサンプルされたテクスチャに描画し、カメラスタックの最後にあるテクスチャに解決します。
  • モバイルでは、multisample auto resolve エクステンションを使用すると、テクスチャが暗示的にフラッシュされたり、テクスチャへの描画以外の操作のソースやデスティネーションとして使用されたときに、テクスチャが暗示的に解決されます。EXT_multisampled_render_to_texture 拡張を参照してください。
  • ディファードレンダリング、HDR、コマンドバッファにより、Unity が中間テクスチャに描画します。
  • 画面のサブセットにレンダリングする場合、Unity は中間テクスチャに描画します。
  • “テクスチャレイアウト” が Unity のレンダリング先と一致しない場合 (例えば、Unity は各目を別々のテクスチャにレンダリングしているのに、RenderPass はテクスチャ配列または単一のテクスチャにレンダリングするように設定されている場合)。
  • kUnityXRRenderTextureFlagsLockedWidthHeight フラグが設定されていて、renderScaleが 1.0 でない場合。
  • kUnityXRenderTextureFlagsWriteOnly フラグが設定されていて、Unity がテクスチャから読み直す必要がある場合。

MSAA

PC でもモバイルでも、エンジンは常にプロバイダーのテクスチャに解決します。エンジンは、暗示的な解決 (テクスチャ拡張のためのマルチサンプルレンダリングがあるモバイルの場合) または明示的な解決を行います。

モバイルでは、プロバイダーは kUnityXRenderTextureFlagsAutoResolve フラグを有効にして、1 サンプルでテクスチャを作成します。

sRGB

UnityXRFrameSetupHints.appSetup.sRGB を使用して、Unity が sRGB テクスチャ形式へのレンダリングを期待しているかどうかをチェックします。プロバイダーは最終的に、 UnityXRRenderTextureDesccolorFormat フィールドから出力テクスチャ形式を選択します。形式が sRGB タイプの場合、Unity はアクティブなプロジェクトが選択している色空間に応じて、sRGB ライトをオンまたはオフにします。コンポジターで sRGB からリニアへの変換を行う sRGB テクスチャから常にサンプリングする必要があります。

深度テクスチャ

SDK が深度情報を必要とする場合は、前述のカラーバッファと同じ方法で深度バッファを取得することができます。UnityXRRenderTextureDesc 上の nativeDepthTex 値は、ネイティブのリソースを指定します。デフォルトでは、nativeDepthTexkUnityXRRenderTextureIdDontCare に設定されている場合、類似した Desc を持つテクスチャ間で深度バッファを共有しようとします。

SDK が深度情報を必要としない場合は、UnityXRRenderTextureDesc::depthFormatkUnityXRDepthTextureFormatNone に設定して、不要な解決を避ける必要があります。

ダブル、トリプルバッファリングの処理

送信時 (後述の 着信未確認中のフレームの送信 セクションを参照) に、Unity がレンダリングする画像を SDK がダブルバッファまたはトリプルバッファにする必要がある場合に対処するために、フレームごとに異なるテクスチャ ID を指定できます。 プロバイダープラグインは、UnityXRRenderTextureId のコレクションを管理します。

フレームのライフサイクル

フレームのライフサイクルを担当するメソッドは 2 つあります。レンダリング開始直前に発生する PopulateNextFrameDesc と、レンダリング完了直後に発生する SubmitCurrentFrame です。どちらのメソッドもグラフィックススレッドで呼び出されます。

PopulateNextFrameDesc の間、ディスプレイプロバイダーは以下のことを行うことが期待されます。

  • cadence を待機 (Unity が再びレンダリングコマンドの送信を開始すべき時まで待機)。あるいは、これを SubmitCurrentFrame で行うこともできます。
  • 次の画像を取得し、次にレンダリングするテクスチャ ID を nextFrame パラメーターで Unity に伝えます。

SubmitCurrentFrame メソッドの中で、ディスプレイプロバイダーは以下のことを行うことが期待されます。

  • 画面に表示する着信未確認中の最後のフレーム (例えば、目のテクスチャや奥行きのテクスチャ) を送信します。
  • PopulateNextFrameDescを待つ代わりに、cadence を待機。

Cadence のためのブロッキング

HMD のディスプレイにレンダリングする際に、可能な限り待機を少なくし、最大のスループットを維持するためには、ポーズの取得やテクスチャの送信などのタイミングを正確に取る必要があります。それぞれの HMD には、コンポジターが動作するネイティブの最新情報に更新するレートがあります。それ以上の速度でレンダリングすると、タイミングが合わなかったり、作業が重複したりして、最適ではなくなってしまいます。

Unity は、フレームのライフサイクルの間、ディスプレイプロバイダーがブロックすること、つまりフレームの cadence を待つことを期待します。Unity は、ブロッキングコールから “目覚めた” 直後にレンダリングコマンドの送信を開始します。“目覚めた” 時刻は、特定のウィンドウで同期させる必要があります。いくつかの SDK では、ヒューリスティックに基づいたフローティングウェイクアップタイムウィンドウを提供しています。Oculus では、これを “queue ahead” と呼んでいます (詳しくは、Oculus 開発者ドキュメント を参照してください)。Valve 社はこれを “Running start” と呼んでいます (このプレゼンテーション のスライド 18 と 19 を参照してください。])

Unity は、フレームのライフサイクルが完了するのを待ってから、ポーズに依存したグラフィックスコマンドの送信を開始します。

Cadence を待つ位置

プロバイダーは、PopulateNextFrameDesc または SubmitCurrentFrame のいずれかで cadence を待つことができます。

Unity がグラフィックススレッドでフレームのグラフィックスコマンドを送信する間、次のフレームのシミュレーションループはメインスレッドで実行されます。それには、物理演算、スクリプトロジックなどが含まれます。PopulateNextFrameDesc は、すべてのレンダリングコマンドが送信された後、次のフレームのシミュレーションと、そのフレームでスケジューリングされたすべてのグラフィックスジョブが完了してから、グラフィックススレッドで呼び出されます。PopulateNextFrameDesc が待機するグラフィックスジョブの 1 つは、現在のフレームの SubmitCurrentFrame です。これが、 SubmitCurrentFrame で cadence を待つことが有効な理由です。さらに、Unity は、 PopulateNextFrameDesc が完了するまで、レンダリングを開始しません。

これらの詳細を考慮すると、SubmitCurrentFrame で cadence を待つことと、 PopulateNextFrameDesc で cadence を待つことには、いくつかの妥協点があります。例えば、SubmitCurrentFrame で cadence を待つと、シミュレーション中にアプリケーションが負荷の高いグラフィックスジョブをスケジュールしている場合、パフォーマンスに問題が生じます。SubmitCurrentFrame はレンダリング後に実行されるようにスケジュールされているため、アプリケーションがスケジュールしたグラフィックスジョブは、SubmitCurrentFrame の後、 PopulateNextFrameDesc の前に実行されることになります。この場合、プロバイダーは SubmitCurrentFrame で待機しており、その後、Unity がレンダリングを開始することを期待して目を覚まします。ただし、Unity は、PopulateNextFrameDesc を呼び出す前に、アプリケーションがスケジューリングしたグラフィックスジョブを処理し、その結果、Unity がレンダリングを開始できるようになります。この、レンダリングのために目覚めてから、update メソッドでスケジュールされたグラフィックスジョブを処理するまでに発生する遅延が、待機の原因となります。開発者は、レンダリング後 にグラフィックスジョブをスケジュールし、グラフィックスジョブを SubmitCurrentFrame の前にスケジュールすることによって、この問題を最適化することができます。

プロバイダーが SubmitCurrentFrame の cadence を待機している間に、PopulateNextFrameDesc で cadence を待機し、Unity のメインスレッドを完全にブロックして、グラフィックスジョブの計算をメインスレッドと並行して実行することは可能です。これは、シミュレーションや他のグラフィックスジョブがすでに完了しているため、問題ありません。問題は、シミュレーションやグラフィックスのスレッドがあまりにも多くの時間を占め、デバイスのターゲットフレームレートを超えてしまう場合に発生します。この場合、PopulateNextFrameDesc が cadence の次のサイクルを待っている間、フレームレートが半分になってしまいます。

着信未確認中のフレームの提出

Unity が SubmitCurrentFrame を呼び出すと、最後のフレームで設定したテクスチャがレンダリングされるか、Unity がグラフィックスドライバーにレンダーコマンドを送信してテクスチャをレンダリングすることになります。Unity はこれでレンダリングを終了したので、コンポジターに渡すことができます。

次のフレーム記述子への記入

レンダリングする次のフレームをブロックまたは取得した後、次のフレームでどのテクスチャにレンダリングするかや、レンダリングパスのレイアウトを Unity に伝える必要があります (後述の “レンダーパス” を参照)。

レンダーパス

UnityXRRenderPass には、カリングパスとシーングラフのトラバーサルが含まれることがあります。これはリソースを大量に消費する操作なので、シングルパスレンダリングのようなトリックを使って、Unityがこの操作を行う回数を制限するようにしてください。

UnityXRRenderPass には、出力テクスチャ (テクスチャ配列でもよい) と、出力の UnityXRRenderParams (ビュー、投影マトリクス、レンダリング先の矩形、またはテクスチャ配列スライスなど) が含まれます。

各フレームごとに、ディスプレイプロバイダーは UnityXRRenderPass を設定し、Unity が次のフレームにレンダリングする UnityXRRenderTextureId を記入します。

UnityXRRenderPass の使用例は以下の通りです。

  • 2 パスのステレオレンダリング (2 RenderPass x 1 RenderParams)
  • シングルパスステレオレンダリング (1 RenderPass x 2 RenderParams)

API はこれらの追加ケースをサポートします (ただし、現時点では Unity が正しく反応しない可能性があります)。

  • クアッドパスワイド FOV ステレオレンダリング (4 RenderPass x 1 RenderParams)
  • シングルパス + ワイド FOV ステレオレンダリング (1 RenderPass x 2 RenderParams + 2 RenderPass x 1 RenderParams)
  • 中心窩 (Foveated) レンダリング
    • それぞれの目に 2 つの独立したテクスチャ (内側と外側)
    • 1 つのテクスチャ、UV フィッティング
  • 外景シナリオ (HoloLens BEV、Mayo 3rd eye)(追加レンダーパス)
  • シーン内のオブジェクトや人物を合成するためのニア/ファーレンダリング (2 つのレンダーパス、異なる投影、異なるターゲット)。

以下の仮定は安全と言えます

  • 各パスに 1 つのテクスチャ (1 つのパスは 1 つのテクスチャ配列)
  • シングルパスの場合、パスごとに 2 つのポーズ/投影 (または、中心窩レンダリング)。

ノート: シングルパスレンダリングの設定はユーザーのシェーダーに影響するため、Unity プロジェクトと XR SDK は同じ設定 (有効/無効) を使用する必要があります。シングルパスレンダリングが有効になっているかどうかを確認するには、 UnityXRFrameSetupHints.appSetup.singlePassRendering を使用します。

カリングパス

2 つのレンダリングパスは、それらの cullingPassIndex が同じ値に設定されている場合、カリングパスを共有することができます。cullingPassIndex は、どの UnityXRCullingPass を使用するかを選択します。カリングパスは、UnityXRNextFrameDesc に記入する必要があります。

XR SDK 入力サブシステム
XR SDK メッシュ化サブシステム