UIテクスチャのランタイム解像度切り替えによるVRAM消費量削減について

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

UE5.7を使用してUIの開発を行っています。

現在、地図画面を実装しています。

複数枚のUIテクスチャを並べて地図を構成し、ウイジェットのスケールやトランスレーションを動的に変更することで、地図の拡大・縮小や移動を実現しています。地図は表示領域よりも広く、拡大率や表示位置によっては画面内に表示されないUIテクスチャが存在します。

この実装方法では、高解像度のテクスチャを常駐させる必要があるため、VRAM消費量が課題となっています。

そこで、通常時は低解像度のテクスチャを常駐させ、地図画面を開いた際に画面内に表示されるテクスチャのみをランタイムで高解像度へ切り替える方式を検討しています。

具体的には、対象のテクスチャでMipMapを有効にした上で、エンジン改造によりUTextureLODSettings::CalculateLODBiasの戻り値を変更し、その後手動でUTexture::UpdateResourceを呼び出すことで、使用するMipレベルを切り替えることを考えています。

こちらについて以下のTexture Streaming有効/無効の2つの方法で検証しましたが、それぞれ問題が発生している状態になります。

■方法①:Texture Streaming無効(デフォルト)

見た目の挙動は問題ないものの、UTexture::UpdateResourceによる切り替え直後、約1秒間は切り替え前後の両方のテクスチャリソースがVRAMにロードされているような挙動を確認しました。

例えばLOD Biasを0から2に切り替えると、Mip0のテクスチャリソース(Mip1以降を含む)とMip2のテクスチャリソース(Mip3以降を含む)が一時的に共存し、Mip2以降が二重にVRAMにロードされた状態になります。

■方法②:Texture Streaming有効(追加のエンジン改造)

エンジン改造によりTEXTUREGROUP_UIでもUTexture::IsPossibleToStream()がfalseを返さないようにした上で、テクスチャサイズを2のべき乗にしました。

これにより、UTexture::UpdateResourceによる切り替え直後のVRAM消費量の増加は抑えられましたが、一時的に(地図画面としては許容できないほど)非常に低い解像度のMipが表示されました。

これに加え、テクスチャサイズを2のべき乗にするためにテクスチャサイズが大きくなり、VRAM消費量が増加することが懸念されます。(2のべき乗でない場合は方法①と同様の挙動になりました)

​​

そこで、以下の2点について質問させてください。

  1. 今回の用途としては方法①と②どちらが適切でしょうか、もしくは他により良い方法があるでしょうか。
  2. ①か②のいずれかが望ましい場合は今の問題をどのようにすれば解決できるでしょうか。
    [Attachment Removed]

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

ご質問ありがとうございます。

・①については、LOD Biasを動的に変更し適用しようとした場合、同一のリソース更新ではなく、「旧リソースの破棄・新リソースの生成」が発生します。リソースの破棄には少し時間がかかるため、VRAMの消費がダブってしまう期間がどうしても存在してしまいます。

・②については、UTexture::UpdateResource() で常駐Mipが最低レベルにリセットされるため、一時的に低解像度に落ちてしまったのだと思われます。テクスチャサイズが「2のべき乗」でない場合はストリーミングが機能しませんので、①のシナリオをなぞってしまうのは、想定通りです。

従いまして、一番目の質問に関しては、①と②のどちらの方法も適切ではないといえます。

解決方法としては、

・a. 【非ストリーム」】低解像度版と高解像度版のテクスチャアセットを別物にして、Mipレベルではなく2解像度レベルの切り替えを独自に実装する

・b. 【ストリーム】Num Cinematic Mip Levels を使用した最小Mipレベルの動的切り替え

が考えられます。aは文字通りなのですが、bについて少しご説明します。

テクスチャエディタでNum Cinematic Mip Levels に数値を指定しますと、インゲームの通常のテクスチャのMipレベルの下限が指定数値ぶん押し込まれます。たとえば3と入れると、トップ3のMipは読み込まれなくなります。これはランタイムでの規制ですので、クックに影響は与えません。

そして高解像度が必要になった場合は、テクスチャオブジェクトのSetForceMipLevelsToBeResident()を使うことで、Cinematic Mipとして抑制していた高解像度Mipがすべて読み込まれます。

この処理はMipの追加ロードとなるため、②で観測されたような低解像度化を起こしません。Cinematic Mipはカットシーン用の機能であることから、SetForceMipLevelsToBeResident()では有効期間を秒数で指定するという少し見慣れないメソッドとなっており、ゲームで使用するには少し改造が必要かもしれません。

いずれにせよ、SetForceMipLevelsToBeResident()の効果が切れれば再び Cinematic Mip の押し込み効果により低解像度Mipに戻ります。このときメモリプレッシャー次第で、読み込んだ高解像度Mip部分はストリームアウトするはずです。

ただし、ストリーミング機能前提ですので、テクスチャの縦横サイズがどちらも「2のべき乗」必須となります。(縦と横でサイズが異なっていても構いません)。UIグループで機能させるには改造が必要なため、Worldグループなどに割り当てて動かすのも一つの手かと思います。

一度ご検討ください。

以上、よろしくお願いいたします。

[Attachment Removed]