すぎしーのXRと3DCG

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

【Unity】Physics.Raycast のパフォーマンスはシーンに存在するColliderの数に影響するのか

f:id:tsgcpp:20200801155055j:plain

概要

Physics.Raycast のパフォーマンスはシーンに存在するColliderの数に影響するのかを調べてみました。

経緯はRaycastを使用するスクリプトを書いているときに、ふと気になったからです。

動作環境

  • Unity 2020.1.0f1
  • Windows 10 (PC)
    • 同一マシンにて検証
    • CPU + GPUで検証 (スペック詳細は割愛)

Raycastについて

Raycastのアルゴリズムは「Rayの直線とColliderが交わるかを調べる」、そして「Rayの開始位置に近いColliderを選ぶ」ことです。
逆に言えばRayとすべてのColliderを検証する必要があるため、Colliderの数が多ければ多いほどRaycastのパフォーマンスは悪化すると考えられます。

検証の集計に関して

今回は簡易的に調査結果を記載します。

本格的な調査としてのデータが欲しい場合は検証対象を複数回行い、その平均を取ることが望ましいです。

検証

検証方法

  • Profilerを使用してPhysics.Raycastメソッドの時間を計測
  • Physics.Raycastは1サンプリングに10000回行う
    • 1サンプリングに1回だと差異が小さすぎるため
  • Build and Runより検証
    • Playだと同一シーンでも差が出てしまったため

以下はコードのイメージ

Profiler.BeginSample(samplingName);
for (int i = 0; i < 10000; ++i) {
    Physics.Raycast(...);
}
Profiler.EndSample();

f:id:tsgcpp:20200801164632j:plain

使用するCollider

  • CapsuleColliderを使用
  • Rigidbodyを付与、かつuseGravity=false
    • 動的Colliderとして設定

f:id:tsgcpp:20200731224835j:plain

検証1. ボックス状にコライダーを複数配置し、Raycastの時間を検証

以下のようにコライダーとRaycastを配置し、Raycastの時間を検証

  • ボックス状にコライダーを配置
  • 中心付近と8隅のColliderに真っ先に当たるRaycastを用意
    • Colliderの検証順番がワールド座標でソートされている可能性を考慮
  • どのColliderにも当たらないRaycastを用意
  • RaycastのMaxDistance: 10000で固定

f:id:tsgcpp:20200731222025j:plain
ボックス上にColliderを配置

f:id:tsgcpp:20200731224222j:plain
中心付近のColliderへのRaycast

f:id:tsgcpp:20200731224242j:plain
どのColliderにも当たらないRaycast

2 x 2 x 2 合計1000の結果

f:id:tsgcpp:20200801155206j:plain

f:id:tsgcpp:20200801160845j:plain

10 x 10 x 10 合計1000の結果

f:id:tsgcpp:20200801155224j:plain

f:id:tsgcpp:20200801160852j:plain

40 x 40 x 40 合計8000の結果

f:id:tsgcpp:20200801155246j:plain

f:id:tsgcpp:20200801160901j:plain

検証1での考察

40x40x40のパターンは10x10x10の64倍のCollider数の差があるにも関わらず、Timeの増加はそれほど大きくありませんね。 また、特徴的な点として「どのColliderにも当たらないRaycast (Raycast to Empty)」は、他のRaycastと比べるとTimeが短いですね。

Physics.Raycastはoriginに最も近いColliderを返すため、距離でのソート処理が発生すると考えられます。
Rayと交わるColliderが1つもない場合はソート処理が発生しないのでTimeも短くなるのかもしれません。

よって「Raycastでソート処理対象のColliderの数」は「シーンにあるColliderの数」よりさらにパフォーマンスに大きく影響するのかもしれません。

GPU Usageの確認

今回の時間の差異の様子からシングルスレッドではなくマルチスレッドで処理されていると予想しGPU Usageを確認してみました。

f:id:tsgcpp:20200801161050j:plain

Colliderの数の差がはっきりと現れていますね。
GPU側で各ColliderごとにRaycastの並列処理を行っていると思われます。

検証2. ソート対象になるようにColliderを配置し、Raycastの時間を検証

  • Rayの直線状に並ぶColliderの数を変える

f:id:tsgcpp:20200801152218j:plain
検証イメージ

Colliderの数(ソート対象)が100の結果

f:id:tsgcpp:20200801174513j:plain

Colliderの数(ソート対象)が1000の結果

f:id:tsgcpp:20200801174525j:plain

Colliderの数(ソート対象)が10000の結果

f:id:tsgcpp:20200801174533j:plain

検証2の考察

検証結果からRaycast時のソート対象が多いほど、Physics.Raycastにかかる時間が増える傾向にあると思われます。

検証3. MaxDistanceでソート対象が絞られるようにRaycastを設定し、Raycastの時間を検証

  • MaxDistanceを変える
  • Rayの直線状に並ぶColliderの数は一定
    • 必ずMaxDistanceの範囲を超えて生成されるように配置
    • マージン: 5, Collider数: 10000

f:id:tsgcpp:20200801161436j:plain
検証イメージ

MaxDistance: 100(ソート対象は約20)

f:id:tsgcpp:20200801173053j:plain

MaxDistance: 1000(ソート対象は約200)

f:id:tsgcpp:20200801173502j:plain

MaxDistance: 10000(ソート対象は約2000)

f:id:tsgcpp:20200801173112j:plain

検証3の考察

ソート対象の数に比例してTimeの増加しているため、やはりソート対象が多いとPhysics.Raycastにかかる時間が増えると思われます。

調査結果のまとめ考察

※PCの場合での考察

  • Physics.Raycastの時間は以下のような傾向がある
    • シーンに存在するColliderの数に少し依存する
    • originに最も近いColliderを算出する際のソート対象数に大きく依存する
    • ソート対象なしの場合、早く終了する
  • Physics.Raycast1回のコストはそれほど大きくない
    • 今回の検証が10000回の合計であることを考えると、1回あたりの時間は大きくないと考える
  • Physics.RaycastGPUのコストはシーンに存在するCollider数に依存

余談:謎の高負荷が起きる現象

以下のようにソート対象がないにも関わらず高負荷になるパターンがありました。
調査してみましたが残念ながら原因は不明です。。。

現象が起きるパターンは以下の画像のようにRaycastをする場合です。
Profilerでは「Raycaster maxDistance 1000.0 Ex」という名前で表示されています。

f:id:tsgcpp:20200801181433j:plain

f:id:tsgcpp:20200801181447j:plain

うまく説明はできませんが、発生する条件は以下のような感じです。

  • Collider群のうち、X座標がColliderたちの中で端よりの内側
    • 逆に外側だとソート対象なしと同様になります。。。
  • ColliderとColliderの間にRayの直線が通るようにRaycast
  • 1つでもColliderにぶつかるようにすると解消される
    • 「Raycaster maxDistance 1000.0 Ex」の目の前にColliderを配置するなど

バグなのかどうかは不明ですが、判明したことがあれば共有します。

今後やってみたいこと

  • 上記謎現象の原因解明
  • TerrainなどのMeshColliderの場合での検証
  • プラットフォーム毎のPhysics.Raycastの差異

今回使用したプロジェクト

github.com

雑談

Raycastのパフォーマンスが気になって調査してみましたが、よく言われているみたいに時間的には低コストみたいで安心しました。
RaycastのアルゴリズムGPU向け処理などの最適化を裏側でやってくれているので、Physics.Raycastのありがたさを改めて実感した気がします。

ただ、Oculus Questなどのモバイル端末だと変わってきそうなので、日を見て調査してみようと思います。

また、まだ謎な部分もあるのでもし何かご存知の方がいらっしゃいましたら共有いただけると嬉しいです!

それでは~