すぎしーのXRと3DCG

主にXR, Unity, 3DCG系の記事を投稿していきます。

【Unity】Physics.SyncTransforms の特性調査

今回は Physics.SyncTransforms の特性を調査したため共有します。

経緯

  • Colliderの物理エンジンへのFlushを 通常のタイミングとは別で実施したい
  • Flushを任意のタイミングで実行するにはPhysics.SyncTransforms をコールする必要があった
  • Physics.SyncTransformsを通常のFlushとは別に実施する場合にパフォーマンスなどが気になったため調査

※Flushとは実際にデータを反映する処理のこと(ここでは物理エンジン用メモリ領域への反映)

検証環境

  • Unity 2020.3.4f1
    • Mono
    • .Net Standard 2.0
  • Physics.autoSyncTransforms = false
  • Oculus Quest 2 (Android)
  • Development Build 有り(Profiler用)

前知識

物理オブジェクトの物理エンジンへのFlush

Physics.SyncTransforms

  • 上記「物理オブジェクトの物理エンジンへのFlush」を任意のタイミングで発動させるメソッド
  • 今回の主役

Physics.autoSyncTransforms

  • ColliderのTransformが変更された際に即座に物理エンジンに反映するかの設定
  • Unity 2019以降はデフォルトでfalse
    • つまりデフォルトではFixedUpdate後にFlushが実施されると同義
  • ProjectSettingsのPhysicsからも設定可能

When autoSyncTransforms is set to true, repeatedly changing a Transform and then performing a physics query can cause performance loss.
  • Physics.autoSyncTransforms = trueにする場合はオーバーヘッドに注意


今回は Physics.autoSyncTransforms = false の場合をメインに調査

Flushの発生条件

  • ColliderのTransformが変更された場合
    • Dirtyフラグが付き、後述の「Flushの発生タイミング」で物理エンジンに反映
  • こちらのUnity Pro Tips によれば移動する物理オブジェクトがない場合はオーバーヘッドはほとんどないとのこと
    • つまり移動しないオブジェクトに関しては static かどうかにかかわらず Flush が発生しない

Flushの発生タイミング

  • Physics.autoSyncTransforms = false であれば FixedUpdate後 or Physics.SyncTransforms コール時点
    • Dirtyフラグが付いた物理オブジェクトのみ
  • Physics.autoSyncTransforms = true であればColliderのTransformが変更された時点


-- ここまでが前知識 --


登場人物

PlayerColliders

  • CapsuleCollider + BoxCollider 64個 (4 x 4 x 4) をアニメーション
  • LateUpdate(Animation Update後として) にて Physics.SyncTransforms を実施し時間を計測
  • すべての検証環境で使用

www.youtube.com

AnimationColliders

  • BoxColliderを大量(64000個)に配置してプレイヤー同様にアニメーション
    • AnimatorのUpdate Mode は Normal

www.youtube.com

PhysicsAnimationColliders

  • AnimationColliders の Update Mode を Animate Physics に変更したもの

DisabledAnimationColliders

  • AnimationCollidersのコライダー群のルートのGameObject(ColliderRoot)をオフにしたもの

DisabledAnimationColliders + CapsuleCollider

  • DisabledAnimationCollidersのColliderRootと同階層に1つのCapsuleColliderを配置したもの

ColliderDisabledAnimationColliders

  • ColliderRoot以下のすべてのColliderに対してCollider.enabled = false にしたもの
    • ColliderRootオブジェクト自体はアクティブのまま

Static Colliders

  • 10000個のstaticのBoxCollider群


検証方法

  • 後述する「登場人物」を組み合わせた状態で Physics.SyncTransforms をコールして実行時間の変化を計測
    • コールはLateUpdate時 (Animatorが終了した後として)

検証結果

※おおよその平均

注目したい部分は色付き

検証対象 時間 (ms)
PlayerColliders x1 0.07
PlayerColliders x2 0.10
PlayerColliders x4 0.19
PlayerColliders x1 (Animator disabled) 0.00
PlayerColliders + AnimationColliders 21.01
PlayerColliders + PhysicsAnimationColliders 0.07
PlayerColliders + DisabledAnimationColliders 0.07
PlayerColliders + DisabledAnimationColliders + CapsuleCollider 0.41
PlayerColliders + ColliderDisabledAnimationColliders 0.08
PlayerColliders + StaticColliders 0.07

クリックで展開:各調査ごとのProfiler

PlayerColliders x1

PlayerColliders x2

PlayerColliders x4

PlayerColliders のみ (Animatorをオフ)

PlayerColliders + AnimationColliders

PlayerColliders + PhysicsAnimationColliders

PlayerColliders + DisabledAnimationColliders

PlayerColliders + DisabledAnimationColliders + CapsuleCollider

PlayerColliders + ColliderDisabledAnimationColliders

PlayerColliders + StaticColliders

注意事項

  • 今回は Physics.SyncTransforms に主眼を置くためCPU Usageはあまり考慮しない
  • XR環境のためCPU Usageに表示されている時間の大部分は Gfx.WaitForRenderThread

判明した特性

  • Physics.SyncTransformsの時間はコール時点のFlushされていないオブジェクト数に依存
    • FixedUpdate直後からPhysics.SyncTransformsをコールするまでにTransformが変更されたオブジェクトが対象
  • Flush済みのオブジェクトはPhysics.SyncTransformsの時間には大して影響はしない
    • つまりTransformが変更されたオブジェクトがなければオーバーヘッドは小さい
  • 通常Flushとそれ以外でFlushする対象は分けることは一応可能
    • ただし「特定のオブジェクトのみFlushしたい」の様なコントロールは不可
      • Unity側に機能が用意されていない(と思われる、ご存じの方がいたら教えてください!)
  • 非アクティブなCollider(コンポーネントのenabled=false含む)はFlushの対象外
    • アクティブ化した後のFixedUpdate後 or Physics.SyncTransforms で改めてFlush
  • 同階層にアクティブと非アクティブ両方のColliderが存在する場合は特殊なFlushとなる可能性あり(詳細は不明)
    • "DisabledAnimationColliders + CapsuleCollider" の時間を見るとCapsuleCollider1つだけFlushされるにしては時間がかかっている

考察

  • Physics.SyncTransforms 時にFlushされるCollider数をうまくコントロールすれば、パフォーマンスへの影響をコントロールできそう
  • UpdateやFixedUpdateなで移動するColliderが多い場合は実行時間に注意が必要になると思われる
    • あまりに多いと Physics.SyncTransforms 時スパイクが発生

検証プロジェクト

github.com

雑感

ちょっと個人的な事情があって検証、ついでにドキュメント化してみました。
Flush対象を明示的に指定できないのはちょっと残念ですね。。。
ただ、物理で動くものとそれ以外で分けるのは一応できそうってとこでしょうか。

また、別の機会に Physics.SyncTransforms を利用したものを作ってみようと思います。

それでは~