トマシープが学ぶ

Unity/VR/AR/デザイン好きのミーハー 記事内容は自分用のメモです

【Hololens2】CPU,GPUのパフォーマンスについて学ぶ【公式ドキュメント読むシリーズ】

シリーズ第3弾はCPU,GPUなどのパフォーマンス!

https://partner.microsoft.com/ja-JP/asset/collection/managing-performance-of-a-quality-hololens-2-application#/

第1弾:デザイン

bibinbaleo.hatenablog.com

第2弾:物理環境(空間マップとか)

bibinbaleo.hatenablog.com

安定性

docs.microsoft.com

60fpsを保とう

用語

  • Accuracy 固定した場所にとどまり続ける
  • Jitter ホログラムの高周波数のぶれ。環境トラッキングが低下したときに発生する
  • Judder 動きが不均一になったり、ホログラムのイメージが二重になる。動きのあるホログラムで特に顕著
  • Drift. ホログラムが元々配置されていた場所から遠ざかっているように見える現象
  • Jumpiness. トラッキングが環境の最新の理解に合わせてホログラムを調整するとホログラムがジャンプして動く
  • Swim ユーザーの頭の動きに応じてホログラムが揺れるように見える
  • Color separation 色分離

フレームレート

HoloLensのディスプレイは1秒に240回更新され、新しくレンダリングされた各イメージの4つの個別のカラーフィールドを表示し、60 FPS(フレーム/秒)のUXを実現

60fpsを維持する=16ミリ秒ごとに新しいイメージをオペレーティングシステムに常に提供する

HoloLensは、画像がディスプレイに表示されるときにユーザーの頭がどこにあるかを予測する。この予測アルゴリズムは概算
予測された頭の位置と実際の頭の位置との間の不一致を説明するためにレンダリングされた画像を調整するハードウェアがある

 

レイテンシを削減する。一定になるようにする

ベンチマークツール

レンダリング距離

HoloLensディスプレイはユーザーから約2.0mの光学距離に固定されているため、HoloLensを着用しているユーザーは常に2.0mに対応して鮮明な画像を維持します

なのでコンテンツを可能な限り2.0mに近づけることで、輻輳と収容の競合による不快感を回避または最小限に抑えることができます

クリッピング(手前に近づくと消えるやつ)

コンテンツのフェードアウトが1mから始まるり、85cmで完全に消えるようにする。

ホログラムとユーザーの両方が静止しているアプリケーションでは、ホログラムを50cm近くまで快適に表示できる。

このときはコンテンツから30cm以内でクリッピングする。フェードアウトはクリッピングの10cm以上離す。(例えば50cmのところにホログラムがあって25cmで完全に消えるなら35cm地点からフェードアウトするようにする)

f:id:bibinbaleo:20200414142526p:plain

再投影

深度再投影、自動平面再投影、平面再投影がある。

(よくわからないけどスタビライゼーションの手法のこと?)

深度が設定されているかはDevicePortalのShoe depth~にチェックを入れると見れる

f:id:bibinbaleo:20200414144151p:plain

設定されているものは青く、されていないものは赤く表示される。(ほとんど青かったけど、アプリを立ち上げたときに出るUnityロゴは赤かった)

色分解

赤、緑、青でできているのでそれが分かれて見えてしまう。特に白はすべての色を使っているので起こりやすい。

f:id:bibinbaleo:20200414145055p:plain

カーソルなどの高速で移動するオブジェクトや、安定化平面から大幅に離れているオブジェクトで起こる。

弱める方法

・頭に追従するものの場合、慣性を持ってばねのように少し遅れてついてくるようにする

・ユーザーが目で追う移動する物体は移動速度5度/秒未満に保つ

・カーソルにライトを使用する。

・ユーザーが見ているホログラムと一致するように安定化平面を調整

・オブジェクトを赤、緑、青にする

・ぼやけたコンテンツにする。(丸い白いカーソルを、動きの方向を向いたわずかにぼやけた線にするなど)

プロファイラー

microsoft.github.io

これの見方

f:id:bibinbaleo:20200414151737p:plain

左上fps(CPUで費やされたフレーム時間)

60 fps:16.6ミリ秒までならいい

 

真ん中の色とりどりの□はフレームレートの履歴。

 

下の2行は(Used Peak Limitと青と黄色の棒グラフ)はメモリ使用率

f:id:bibinbaleo:20200414152219p:plain

パフォーマンス

docs.microsoft.com

ボルトネック

  • CPU:ジャスチャーや音声入力などのinput、物理演算などを担当
  • CPU to GPUGPUへ描画呼び出し
  • GPU:3Dデータをピクセルに変換、デバイスに送信する2D画像生成

f:id:bibinbaleo:20200414154637p:plain

分析(プロファイル)

GPU処理が重い(GPUBound)か、CPU処理が重いか(CPUBound)かを判断する方法

レンダーターゲット出力の解像度を下げたあと

fpsが上がる→GPUが原因

fpsが変わらない→CPUが原因

 

UnityだとUnityEngine.XR.XRSettings.renderScale = 0.7f;で解像度を下げれる

この記事で上げる処理はやったことがある

Oculusで文字をきれいに表示する - トマシープが学ぶ

プロファイラーツール

Intel® Graphics Performance Analyzers

software.intel.com

Visual Studio Graphics Debugger

f:id:bibinbaleo:20200414160414p:plain

Unityプロファイラー

techblog.kayac.com

【Unity】Android実機動作でのプロファイラーを見る - トマシープが学ぶ


 Unityフレームデバッガー

docs.unity3d.com

CPU改善法

以下を最適化したらいい

GPU改善法

メモリ帯域幅かフィルレート(塗りつぶし率)に影響を受ける

メモリ帯域幅

GPUがメモリから実行できる読み取りと書き込みの速度

これげ原因かどうかはテクスチャの品質を下げてfpsが向上するかテストするといい

f:id:bibinbaleo:20200414161511p:plain

改善方法

  1. テクスチャ解像度を下げる
  2. 使用するテクスチャを少なくする(法線、鏡面反射光など)
フィルレート

GPUによって1秒あたりに描画できるピクセル

解像度を下げてfps向上するかテストする。

 

改善方法

  1. レンダリング/処理するオブジェクトの数
  2. シェーダーあたりの操作数
  3. 終結果までのGPUステージの数(ジオメトリシェーダー、後処理エフェクトなど)
  4. レンダリングするピクセル数(ディスプレイ解像度)
ポリゴン数削減

減らそう!

オーバードローしない

見えないオブジェクトをレンダリングしない。壁の後ろにあるものなど。

シェーダーを変える

 シェーダーで計算される操作の数を減らすと、フレームあたりのGPUに必要な作業を大幅に減らすことができる

 

  • 可能な場合は常に双一次フィルタリングを使用する
  • 乗算と加算を同時に行うために、MAD組み込み関数を使用するように式を再配置します
  • CPUで可能な限り事前計算し、定数として材料に渡します
  • ピクセルシェーダーから頂点シェーダーへの移動操作を優先する
PostProcessingを使わない

MSAAなどのアンチエイリアシング技術などのポストエフェクトを使わない

ジオメトリ、ハル、コンピュートシェーダなどの追加のシェーダステージも避ける

メモリ管理

過剰なメモリ割り当てと割り当て解除操作は、一貫性のないパフォーマンス、フリーズしたフレーム、その他の有害な動作を引き起こす可能性があり

Unityでのパフォーマンス調整

docs.microsoft.com

プロファイラ

UnityProfilerをみるときは、エディタで見ても実際のランタイム環境を表してないので、デバイス上で実行中にアプリケーションをリモートでプロファイルすることをお勧めする。

UWPで動かすときのマニュアル

docs.unity3d.com

PlayerSettingで以下二つにチェックを入れたら良さそう

f:id:bibinbaleo:20200414172204p:plain

BuildのウィンドウでAutoconnectProfilerにチェックを入れたら自動でプロファイラーウィンドウが開く?

f:id:bibinbaleo:20200414172134p:plain

こちらも詳しい

bluebirdofoz.hatenablog.com

 

(私はプロファイラーでIPアドレス入れてもつながらなかった。IPV4

プロファイラの診断方法チュートリアル(激長英語)

learn.unity.com

CPUのパフォーマンス改善

GetComponent

GetComponentはStart()で一回だけにしろ!

f:id:bibinbaleo:20200414171212p:plain

文字列でGetComponent(文字列)もコストがかかる

(良い)コンポーネントGetComponent(タイプタイプ)
(良い)T GetComponent <T>()
(悪い)コンポーネントGetComponent(string)>

LINQは避ける

アルゴリズムを手動で書き込むよりもはるかに多くの計算、特に多くのメモリ割り当てが必要

Unity APIも避ける

これらもコストがかかる

GameObject.SendMessage()
GameObject.BroadcastMessage()
UnityEngine.Object.Find()
UnityEngine.Object.FindWithTag()
UnityEngine.Object.FindObjectOfType()
UnityEngine.Object.FindObjectsOfType()
UnityEngine.Object.FindGameObjectsWithTag()
UnityEngine.Object.FindGameObjectsWithTag()

 

SendMessage()、SendMessage()も1000倍遅くなる

 

ボックス化

だめ

"null 許容値型" T?

null 許容値型 - C# リファレンス | Microsoft Docs

 

空のコールバック関数

こういうのは消そう

void Update()
{
}

 

フレームごとに1回の実行を優先する操作

初期化時にすべてのオブジェクトをインスタンス

UnityEngine.Object.Instantiate()

 

インターフェースと仮想構造

インターフェースは直接関数を呼び出すよりコストがかかる。

不要なら消去したほうがいいけど、コードの保守性とトレードオフだね

UpdateUI()のようなフレームごとに何度も呼び出されるものは注意

 

値で構造体を渡さない

クラスとは異なり、構造体は値型であり、関数に直接渡されると、その内容は新しく作成されたインスタンスにコピーされます。このコピーは、CPUコストとスタック上のメモリを追加します。小さな構造体の場合、効果は通常非常に小さく、許容範囲です。ただし、すべてのフレームで繰り返し呼び出される関数と、大きな構造体をとる関数の場合、可能であれば、参照によって渡されるように関数定義を変更します。

 

物理

物理に費やす時間または1秒あたりの反復回数を制限すると軽くなるけど、当然シミュレーションの精度は低下する。

Unity - Manual: Time

 

コライダー

メッシュコライダーはコスト型赤いので球とか゚ボックスで置き換えよう。

Sphere < Capsule < Box <<< Mesh (Convex) < Mesh (non-Convex)

unity3d.com

アニメーション

Animatorコンポーネントを無効にして、アイドルアニメーションを無効にする。(GameObjectを非アクティブにしても効果はない)

Animatorが同じものに値を設定するループに座っているようなデザインパターンは避けてください。???

 

複雑なアルゴリズム

インバースキネマティクス、経路探索などの複雑なアルゴリズムを使用している場合は、より単純なアプローチを見つけるか、パフォーマンスに関連する設定を調整してください

 

CPUからGPUのパフォーマンス改善

グラフィックカードに送信される描画呼び出しに依存

描画呼び出しを戦略的にa)削減するか、b)最適な結果を得るために再構成する必要あり

SinglePasInstance

各目の描画呼び出しを1つのインスタンス化描画呼び出しに減らすことができます。2つの描画呼び出し間のキャッシュの一貫性により、GPUのパフォーマンスも向上しています。

f:id:bibinbaleo:20200415101504p:plain

ただし、適応していないシェーダーだと片目でしか描画されないことがあるので対応するべし

bibinbaleo.hatenablog.com

 

静的バッチ

変化しないオブジェクトはstaticにする。

f:id:bibinbaleo:20200415142700p:plain

動的バッチ処理

docs.unity3d.com

 

テクスチャ・メッシュをまとめる

テクスチャをまとめてマテリアルを一つにすべし!これをテクスチャアトラスと呼ぶ

f:id:bibinbaleo:20200415143030p:plain

Texture atlas - Wikipedia

メッシュもGameObject一つにまとめることが望ましい。

 

ツールをboothで配布している方がいた 

booth.pm

GPUのパフォーマンス改善

 DepthBufferSharingを有効にする

16bitのほうがいい。消費電力の削減とパフォーマンスの向上

f:id:bibinbaleo:20200415143519p:plain

ただし16bitにするとZファイティングが起こりやすくなる。Zファイティングとは物が重なってちらちらお互いの表面が見えるあれ。

tsubakit1.hateblo.jp

Holoにおいて解決するには「Unityのデフォルトの1000mではなく50mのファークリッププレーンを使用すると、通常Zファイティングを排除できます。」だって。

f:id:bibinbaleo:20200415144533p:plain

PostProcessingを避ける

フレームごとに数百万回の操作が行われるから

 

照明設定

RealtimeLightingのチェックは外す

f:id:bibinbaleo:20200415145409p:plain

影を落とすのもやめる。

f:id:bibinbaleo:20200415150056p:plain

ライトごとにも設定できるし、全体的に制御することもできる。

f:id:bibinbaleo:20200415150248p:plain

ポリゴンを減らす

  1. シーンからオブジェクトを削除する
  2. 与えられたメッシュのポリゴン数を減らすアセットデシメーション
  3. 同じジオメトリの下位ポリゴンバージョンで遠くのオブジェクトをレンダリングするアプリケーションに詳細レベル(LOD)システムを実装する

シェーダーのパフォーマンス比較

 

マテリアルの右上の歯車からSelectShaderを押す

f:id:bibinbaleo:20200415150520p:plain

「Compiled and show code」を押す

f:id:bibinbaleo:20200415150517p:plain

VidualStudioが開く。Mtoonだとこんな感じ29,165

f:id:bibinbaleo:20200415150909p:plain

「頂点シェーダーとピクセルシェーダーの両方に対するさまざまな操作の数を確認します」Vertex shader 29avg match、Fragment Shader 165avg matchの数がみる。

 

  • Vertex shader=頂点シェーダー(画面に映るメッシュの頂点ごとに実行)
  • Fragment Shaderピクセルシェーダー(ピクセルごとに実行)

 

画面出力が2k x 2k画像の場合、フラグメントシェーダーは2,000 * 2,000 = 4,000,000回実行される可能性があります。2つの目をレンダリングする場合、2つの画面があるため、この数は2倍になる」

なのでピクセルシェーダーの操作数を減らしたほうがいい。

 

UnityStandardシェーダーではなく、MRTKのシェーダーを使おう!

f:id:bibinbaleo:20200415153931p:plain

数字低い!16,40

f:id:bibinbaleo:20200415153934p:plain

UnityStandardシェーダーだとこんな感じ。33,123

f:id:bibinbaleo:20200415154051p:plain

シェーダーをあらかじめロードする機能もある

docs.unity3d.com

 

オーバードローを制限する

オクルージョンカリングしたらいい

docs.unity3d.com

オーバードローはSceneビューの左上の設定で見れる

f:id:bibinbaleo:20200415154518p:plain

重なってるものが見えるということなのかな?

f:id:bibinbaleo:20200415165311p:plain

メモリの良い設定

ガベージコレクション

メモリを開放する作業。頻繁に行うとパフォーマンスが下がる

learn.unity.com

 

オブジェクトプーリング 

 

同一のオブジェクトの大きなプールを割り当て、このプールから非アクティブな利用可能なインスタンスを再利用する

 

オブジェクト プールは、アプリの期間中に可変の寿命を持つ再利用可能なコンポーネントに最適

learn.unity.com

起動

小さいシーンからアプリを開始して、そこから残りのシーンを読み込む。

 

ロード中にチカチカするのを防ぐために

「ロードされているシーンで AsyncOperation.allowSceneActivation プロパティを "false" に設定し、シーンがロードされるのを待ち、画面を黒くしてから "true" に戻してシーンのアクティベーションを完了させることです。

起動シーンがロードされている間は、ホログラフィックのスプラッシュ画面がユーザーに表示されることを覚えておいてください。」