OpenXRアプリを実行した際、カラー画像とDepth情報の間にフレーム単位のずれが発生する

現象

UE5.3およびUE5.6で作成したOpenXRアプリを実行した際、カラー画像とDepth情報の間にフレーム単位のずれが発生するという問題があります。

この現象は常に発生するわけではなく、RenderRHI、GPUの種類、ドライバーバージョンの組み合わせによって発生の有無が異なります

期待する挙動

全てのRHIにおいて、正しいDepth情報がカラー画像と同期し、フレーム単位のずれが発生しないこと。

弊社でも確認したところ以下の3点を課題として考えておりますので、ご確認いただきたいです。

課題の概要図も一緒にご確認いただければと思います。

課題①カラー画像のスワップチェインイメージのインデックス取得

(UnrealEngine5.6)\Engine\Source\Runtime\Engine\Private\Slate\SceneViewport.cpp

// We need to acquire a buffered texture from either the new RT or the existing one

int32 TextureIndex = StereoRenderTargetManager->AcquireColorTexture();

CurrentBufferedTargetIndex = TextureIndex < 0 ? CurrentBufferedTargetIndex : TextureIndex;

にて利用するスワップチェインのインデックスは、

別なタイミングで取得して保持していたインデックスを使っている可能性があります。

結果、Runtimeが利用してほしいインデックスが使われていないため、報告させていただいた現象のフレーム単位のずれにつながる可能性があると考えています。

課題②Depth画像のスワップチェインイメージのインデックス更新タイミング

(UnrealEngine5.6)\Engine\Plugins\Runtime\OpenXR\Source\OpenXRHMD\Private\OpenXRHMD.cpp

OnBeginRendering_RenderThread

OnBeginRendering_RHIThread

RHIスレッドでxrAcquireSwapchainImageを用いてDepth画像のインデックスを取得していますが、

利用するRenderスレッドとの同期がとれていない可能性があるため、

Depth画像のインデックスについても現象のフレーム単位のずれが発生する可能性があると考えています。

課題③OpenXRのSwapchain関連のAPI呼び出し順について

OpenXRの仕様では、xrAcquireSwapchainImage → xrWaitSwapchainImage → xrReleaseSwapchainImageの順番で呼び出すことが仕様上決められていると思います。

UE5.6のコード((UnrealEngine5.6)\Engine\Plugins\Runtime\OpenXR\Source\OpenXRHMD\Private\OpenXRHMD.cppのFOpenXRHMD::OnBeginRendering_RHIThread関数)では

ColorSwapchain->WaitCurrentImage_RHIThread(OPENXR_SWAPCHAIN_WAIT_TIMEOUT)の呼び出し後にColorSwapchain->IncrementSwapChainIndex_RHIThread()を呼びだしており、AcquireとWaitの順が逆転しているようです。

正しいインデックスが取得できない原因になっていると考えます。

[Attachment Removed]

再現手順
下記サイトに記載の手順、かつ、少し重いデータを追加してOpenXRプラグインを使ったアプリを作成した。

そのOpenXRを使ったアプリを実行した際、カラー画像とDepth情報の間にフレーム単位のずれが発生する。

作成手順はUnrealEngine5.3ですが、同じように作成したUnrealEngine5.6で作成したアプリでも発生します。

https://mr.ssw.imaging-saas.canon/mr/tutorial/unreal5-3_MRP2025.html

使用環境

Unreal Engine:5.3、5.6

有効化したPlugin:OpenXR、OpenXRHandTracking

OS:Windows 11

GPU:RTX 4080、RTX 3070 Ti

ドライバーバージョン:566.36、580.97、581.08

[Attachment Removed]

お世話になっております。

課題①②はご指摘の通りで、こちらでも問題として確認できました。課題③については、コードの記述は仰る通りなのですが、これは不具合ではなく意図的な作りになっています。

まず前提として、CVar「xr.OpenXRAcquireMode」のデフォルト値が2(RHIスレッドでのみacquire)になっており、この設定が本現象の起点になっています。

課題①についてですが、このデフォルト設定だとAcquireColorTexture(L2617)は実際にはacquireせず、RHIスレッドが別のタイミングで取ったインデックスをそのまま返しているだけです。GameスレッドがRHIスレッドにちょうど1フレーム遅れていれば辻褄が合うのですが、それを保証する仕組みはどこにもなく、負荷やドライバー、RHIの違いでパイプラインの深さが変わると簡単にずれます。

課題②も同様の構図です。DepthのacquireはRHIスレッド側(OnBeginRendering_RHIThread、L3908-3914)で行われる一方、実際の描画先はRenderスレッドがAllocateDepthTexture(L2860あたり)のGetTextureRef()で取っています。このポインタはacquireのたびに書き換わるので、Renderスレッドは同期なしで最新値を読んでいる状態です。ColorとDepthで読むタイミングが違うため、両者が相対的に1フレームずれ得ます。「環境によって出たり出なかったり」というご報告とも符合します。

課題③ですが、L3903でWait、L3906でIncrementという順序自体はご指摘の通りです。ただこれはパイプライン化の意図的な設計で、スワップチェイン生成時に1枚先行してacquireしておき(Color:L2717-2726、Depth:L2812-2820)、以降は毎フレーム「前回acquireした画像(=今回の描画先)をWaitしてから、次フレーム用に新しくAcquire」という流れになっています。xrWaitSwapchainImageは未Waitの最も古い画像に効くので、画像単位で見ればAcquire→Wait→Releaseの順は守られています。

加えてImageAcquired/ImageReadyのアトミックガードがあるので、仮に順序が崩れてもno-opになるだけです。

まとめると、原因は①②で、「描画に使う画像」と「そのフレームでWait/Releaseされる画像」の対応がスレッド間のタイミング頼みになっている、という点に尽きます。

一度、xr.OpenXRAcquireMode=1を設定して試していただけますか。この設定だとColor/Depthの両方がGameスレッド上で確実にacquireされる(SceneViewport.cpp L1861-1864)ので、上記のずれが解消されるはずです。D3D11/D3D12環境なら通常は問題なく動きます。これで直れば原因の裏付けにもなります。

その確認も兼ねて、2点うかがえればと思います。

ご利用のOpenXRランタイムは何でしょうか。再現手順のURLからCanon MREALかと推測していますが、念のため確認させてください。あと、発生したRHIと発生しなかったRHIの組み合わせも教えていただけると助かります。

本件は既存のチケットが見当たらなかったので、新しく不具合として起票します。番号が出たら本スレッドでお知らせします。

お手数ですが、よろしくお願いします。

[Attachment Removed]

お世話になっております。 ご回答ありがとうございます。

ご教示いただいた、xr.OpenXRAcquireMode=1を試しました。

ターゲットRHIの設定がDirectX12の場合で簡易確認を行ったところ、ずれるフレームが0になったわけではないですが、ずれるフレームの数が激減したようです。

一方で、ターゲットRHIの設定がVulkanの時は異常終了しました。Vulkan利用時の異常終了の原因を調べることは可能でしょうか。

推測していただいた通り、利用しているOpenXRランタイムはCanonのMREALです。少し昔に検証したもののため、GraphicDriverのバージョンが古いのですが、xr.OpenXRAcquireMode=2の時の結果は、以下の通りです。

  • 「DriverVer566.36 結果」 [DefaultRHI:Vulkan]エラーダイアログ「A Compatible Vulkan Driver is required to run the engine.」が表示され起動できない。 [DefaultRHI:D3D12]デプス情報のずれが起きる。 [D3D11]デプス情報のずれは起きない。
  • 「DriverVer581.08 結果」[DefaultRHI:Vulkan]デプス情報のずれが起きる。 [DefaultRHI:D3D12]デプス情報のずれが起きる。 [DefaultRHI:D3D11]デプス情報のずれは起きない。

上記結果を出したときのPCのスペックは以下です。

プロセッサ:Intel(R) Core™ i9-14900KF 3.20 GHz

実装RAM:128 GB

システムの種類:64bit

OS:Win11 23H2

グラフィックスカード:NVIDIA GeForceRTX 4080

また、不具合として起票いただけるとのこと、ありがとうございます。番号作成されましたら、お知らせいただけますと幸いです。

どうぞよろしくお願いいたします。

[Attachment Removed]

お世話になっております。検証ありがとうございます。

ドライバー別の切り分けまでしていただけたので、そのままチケットに載せました。

Vulkanのクラッシュは原因が分かりました。

VulkanだとxrAcquireSwapchainImageがランタイム内部でVkQueueに触ることがあり、この制約はOpenXRHMD_Swapchain.cppのFIXMEコメントにも書かれています。mode=1ではこの呼び出しがGameスレッドから直接走るため、RHI側のキュー使用とぶつかって落ちます。5.6.1ではRHIスレッド側の呼び出しをVulkanのグラフィックスキュー上で実行するよう修正されていますが(RunOnGraphicsQueue)、Gameスレッド側の経路は手当てされていません。デフォルトが2なのはこれが理由です。Vulkanでmode=1は現状使えず、エンジン修正が必要になります。

D3D12でゼロにならない件も想定通りです。mode=1で描画先インデックスの選択という主要なレースは消えますが、2つ残ります。1つはGameスレッドがRHIスレッドのWaitより2フレーム以上先行した場合、acquireが冗長判定でスキップされ(LogHMDのVerboseに「Attempted to redundantly acquire image」が出ます)、連続する2フレームが同じ画像を使ってしまうケース。もう1つはDepthの描画先ポインタをRenderスレッドが同期なしで読んでいる点です。完全に直すにはエンジン側の修正が必要です。

Driver 566.36でVulkanが起動しない件は、UE5.6のVulkan RHIが要求するドライバーバージョンを満たしていないためで、本件とは別です。Vulkanの検証は新しいドライバーでお願いします。

チケットはUE-388831で起票しました。以下のページで進捗をご確認いただけます。ただし公開側の反映には時間がかかることがありますので、動きがあればこちらのスレッドでもお知らせします。

https://issues.unrealengine.com/issue/UE-388831

よろしくお願いいたします。

[Attachment Removed]

UE-388831の調査およびチケット作成ありがとうございました。

VulkanおよびD3D12における挙動や根本原因について詳細にご説明いただき、大変参考になりました。

今後の開発計画を検討する上で、本件の修正予定について確認させていただきたいです。そのため、以下について現時点で分かる範囲でご教示いただけますでしょうか。

1. UE-388831について、Epic Gamesとして修正対応を予定しているという認識でよろしいでしょうか。

2. 修正が予定されている場合、どのUnreal Engineバージョンへの反映を想定されていますでしょうか。

現時点で確定していない内容もあるかと思いますが、概略でも構いませんのでご教示いただけますと幸いです。

引き続きよろしくお願いいたします。

[Attachment Removed]

お世話になっております。

1点目ですが、現時点で修正を確約できる状況ではありません。UE-388831は追加情報待ちのステータスになっています。社内のXRチームがCanon MREALの実機とランタイムを保有しておらず、こちらで再現できないためです。

2点目も同じ理由で、反映先バージョンは未定です。

つきまして、もし可能であれば、VRテンプレートをベースに、一般的に入手可能なヘッドセットとランタイム(SteamVRやMeta Quest Linkなど)で同じ現象が再現できないか、お試しいただけますでしょうか。原因はスレッド間のタイミングにあるため、ランタイム固有の問題ではないと見ています。再現が取れれば調査に着手できます。

また、既に修正案をお持ちでしたら、プルリクエストとしてご提出いただければエンジンチームでレビューします。

お手数ですが、よろしくお願いします。

[Attachment Removed]