シリーズ第3弾はCPU,GPUなどのパフォーマンス!
第1弾:デザイン
第2弾:物理環境(空間マップとか)
安定性
60fpsを保とう
用語
- Accuracy 固定した場所にとどまり続ける
- Jitter ホログラムの高周波数のぶれ。環境トラッキングが低下したときに発生する
- Judder 動きが不均一になったり、ホログラムのイメージが二重になる。動きのあるホログラムで特に顕著
- Drift. ホログラムが元々配置されていた場所から遠ざかっているように見える現象
- Jumpiness. トラッキングが環境の最新の理解に合わせてホログラムを調整するとホログラムがジャンプして動く
- Swim ユーザーの頭の動きに応じてホログラムが揺れるように見える
- Color separation 色分離
フレームレート
HoloLensのディスプレイは1秒に240回更新され、新しくレンダリングされた各イメージの4つの個別のカラーフィールドを表示し、60 FPS(フレーム/秒)のUXを実現
60fpsを維持する=16ミリ秒ごとに新しいイメージをオペレーティングシステムに常に提供する
HoloLensは、画像がディスプレイに表示されるときにユーザーの頭がどこにあるかを予測する。この予測アルゴリズムは概算
予測された頭の位置と実際の頭の位置との間の不一致を説明するためにレンダリングされた画像を調整するハードウェアがある
レイテンシを削減する。一定になるようにする
ベンチマークツール
- GPUView
- Visual Studio Graphics Debugger
- hakase0274.hatenablog.com
- Unityなどの3Dエンジンに組み込まれたプロファイラー
レンダリング距離
HoloLensディスプレイはユーザーから約2.0mの光学距離に固定されているため、HoloLensを着用しているユーザーは常に2.0mに対応して鮮明な画像を維持します
なのでコンテンツを可能な限り2.0mに近づけることで、輻輳と収容の競合による不快感を回避または最小限に抑えることができます
クリッピング(手前に近づくと消えるやつ)
コンテンツのフェードアウトが1mから始まるり、85cmで完全に消えるようにする。
ホログラムとユーザーの両方が静止しているアプリケーションでは、ホログラムを50cm近くまで快適に表示できる。
このときはコンテンツから30cm以内でクリッピングする。フェードアウトはクリッピングの10cm以上離す。(例えば50cmのところにホログラムがあって25cmで完全に消えるなら35cm地点からフェードアウトするようにする)
再投影
深度再投影、自動平面再投影、平面再投影がある。
(よくわからないけどスタビライゼーションの手法のこと?)
深度が設定されているかはDevicePortalのShoe depth~にチェックを入れると見れる
設定されているものは青く、されていないものは赤く表示される。(ほとんど青かったけど、アプリを立ち上げたときに出るUnityロゴは赤かった)
色分解
赤、緑、青でできているのでそれが分かれて見えてしまう。特に白はすべての色を使っているので起こりやすい。
カーソルなどの高速で移動するオブジェクトや、安定化平面から大幅に離れているオブジェクトで起こる。
弱める方法
・頭に追従するものの場合、慣性を持ってばねのように少し遅れてついてくるようにする
・ユーザーが目で追う移動する物体は移動速度を5度/秒未満に保つ
・カーソルにライトを使用する。
・ユーザーが見ているホログラムと一致するように安定化平面を調整
・オブジェクトを赤、緑、青にする
・ぼやけたコンテンツにする。(丸い白いカーソルを、動きの方向を向いたわずかにぼやけた線にするなど)
プロファイラー
これの見方
左上fps(CPUで費やされたフレーム時間)
60 fps:16.6ミリ秒までならいい
真ん中の色とりどりの□はフレームレートの履歴。
下の2行は(Used Peak Limitと青と黄色の棒グラフ)はメモリ使用率
パフォーマンス
ボルトネック
分析(プロファイル)
GPU処理が重い(GPUBound)か、CPU処理が重いか(CPUBound)かを判断する方法
レンダーターゲット出力の解像度を下げたあと
fpsが変わらない→CPUが原因
UnityだとUnityEngine.XR.XRSettings.renderScale = 0.7f;で解像度を下げれる
この記事で上げる処理はやったことがある
プロファイラーツール
Intel® Graphics Performance Analyzers
Visual Studio Graphics Debugger
Unityプロファイラー
【Unity】Android実機動作でのプロファイラーを見る - トマシープが学ぶ
Unityフレームデバッガー
CPU改善法
以下を最適化したらいい
GPU改善法
メモリ帯域幅かフィルレート(塗りつぶし率)に影響を受ける
メモリ帯域幅
GPUがメモリから実行できる読み取りと書き込みの速度
これげ原因かどうかはテクスチャの品質を下げてfpsが向上するかテストするといい
改善方法
- テクスチャ解像度を下げる
- 使用するテクスチャを少なくする(法線、鏡面反射光など)
フィルレート
解像度を下げてfps向上するかテストする。
改善方法
ポリゴン数削減
減らそう!
オーバードローしない
見えないオブジェクトをレンダリングしない。壁の後ろにあるものなど。
シェーダーを変える
シェーダーで計算される操作の数を減らすと、フレームあたりのGPUに必要な作業を大幅に減らすことができる
- 可能な場合は常に双一次フィルタリングを使用する
- 乗算と加算を同時に行うために、MAD組み込み関数を使用するように式を再配置します
- CPUで可能な限り事前計算し、定数として材料に渡します
- ピクセルシェーダーから頂点シェーダーへの移動操作を優先する
PostProcessingを使わない
MSAAなどのアンチエイリアシング技術などのポストエフェクトを使わない
ジオメトリ、ハル、コンピュートシェーダなどの追加のシェーダステージも避ける
メモリ管理
過剰なメモリ割り当てと割り当て解除操作は、一貫性のないパフォーマンス、フリーズしたフレーム、その他の有害な動作を引き起こす可能性があり
Unityでのパフォーマンス調整
プロファイラ
UnityProfilerをみるときは、エディタで見ても実際のランタイム環境を表してないので、デバイス上で実行中にアプリケーションをリモートでプロファイルすることをお勧めする。
UWPで動かすときのマニュアル
PlayerSettingで以下二つにチェックを入れたら良さそう
BuildのウィンドウでAutoconnectProfilerにチェックを入れたら自動でプロファイラーウィンドウが開く?
こちらも詳しい
(私はプロファイラーでIPアドレス入れてもつながらなかった。IPV4)
プロファイラの診断方法チュートリアル(激長英語)
CPUのパフォーマンス改善
GetComponent
GetComponentはStart()で一回だけにしろ!
文字列で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秒あたりの反復回数を制限すると軽くなるけど、当然シミュレーションの精度は低下する。
コライダー
メッシュコライダーはコスト型赤いので球とか゚ボックスで置き換えよう。
Sphere < Capsule < Box <<< Mesh (Convex) < Mesh (non-Convex)
アニメーション
Animatorコンポーネントを無効にして、アイドルアニメーションを無効にする。(GameObjectを非アクティブにしても効果はない)
Animatorが同じものに値を設定するループに座っているようなデザインパターンは避けてください。???
複雑なアルゴリズム
インバースキネマティクス、経路探索などの複雑なアルゴリズムを使用している場合は、より単純なアプローチを見つけるか、パフォーマンスに関連する設定を調整してください
CPUからGPUのパフォーマンス改善
グラフィックカードに送信される描画呼び出しに依存
描画呼び出しを戦略的にa)削減するか、b)最適な結果を得るために再構成する必要あり
SinglePasInstance
各目の描画呼び出しを1つのインスタンス化描画呼び出しに減らすことができます。2つの描画呼び出し間のキャッシュの一貫性により、GPUのパフォーマンスも向上しています。
ただし、適応していないシェーダーだと片目でしか描画されないことがあるので対応するべし
静的バッチ
変化しないオブジェクトはstaticにする。
動的バッチ処理
テクスチャ・メッシュをまとめる
テクスチャをまとめてマテリアルを一つにすべし!これをテクスチャアトラスと呼ぶ
メッシュもGameObject一つにまとめることが望ましい。
ツールをboothで配布している方がいた
GPUのパフォーマンス改善
DepthBufferSharingを有効にする
16bitのほうがいい。消費電力の削減とパフォーマンスの向上
ただし16bitにするとZファイティングが起こりやすくなる。Zファイティングとは物が重なってちらちらお互いの表面が見えるあれ。
Holoにおいて解決するには「Unityのデフォルトの1000mではなく50mのファークリッププレーンを使用すると、通常Zファイティングを排除できます。」だって。
PostProcessingを避ける
フレームごとに数百万回の操作が行われるから
照明設定
RealtimeLightingのチェックは外す
影を落とすのもやめる。
ライトごとにも設定できるし、全体的に制御することもできる。
ポリゴンを減らす
- シーンからオブジェクトを削除する
- 与えられたメッシュのポリゴン数を減らすアセットデシメーション
- 同じジオメトリの下位ポリゴンバージョンで遠くのオブジェクトをレンダリングするアプリケーションに詳細レベル(LOD)システムを実装する
シェーダーのパフォーマンス比較
マテリアルの右上の歯車からSelectShaderを押す
「Compiled and show code」を押す
VidualStudioが開く。Mtoonだとこんな感じ29,165
「頂点シェーダーとピクセルシェーダーの両方に対するさまざまな操作の数を確認します」Vertex shader 29avg match、Fragment Shader 165avg matchの数がみる。
「画面出力が2k x 2k画像の場合、フラグメントシェーダーは2,000 * 2,000 = 4,000,000回実行される可能性があります。2つの目をレンダリングする場合、2つの画面があるため、この数は2倍になる」
なのでピクセルシェーダーの操作数を減らしたほうがいい。
UnityStandardシェーダーではなく、MRTKのシェーダーを使おう!
数字低い!16,40
UnityStandardシェーダーだとこんな感じ。33,123
シェーダーをあらかじめロードする機能もある
オーバードローを制限する
オクルージョンカリングしたらいい
オーバードローはSceneビューの左上の設定で見れる
重なってるものが見えるということなのかな?
メモリの良い設定
メモリを開放する作業。頻繁に行うとパフォーマンスが下がる
- コンポーネントとクラスへの参照をキャッシュする。Start()
- StringBuilder C#クラスを使用して、実行時に複雑な文字列を動的に構築する
- 不要になったDebug.Log()の呼び出しを削除
- ホログラフィックアプリが通常大量のメモリを必要とする場合は、読み込み画面または遷移画面を表示するときなどの読み込みフェーズ中にSystem.GC.Collect()を呼び出す GC.Collect メソッド (System) | Microsoft Docs
オブジェクトプーリング
同一のオブジェクトの大きなプールを割り当て、このプールから非アクティブな利用可能なインスタンスを再利用する
オブジェクト プールは、アプリの期間中に可変の寿命を持つ再利用可能なコンポーネントに最適
起動
小さいシーンからアプリを開始して、そこから残りのシーンを読み込む。
ロード中にチカチカするのを防ぐために
「ロードされているシーンで AsyncOperation.allowSceneActivation プロパティを "false" に設定し、シーンがロードされるのを待ち、画面を黒くしてから "true" に戻してシーンのアクティベーションを完了させることです。
起動シーンがロードされている間は、ホログラフィックのスプラッシュ画面がユーザーに表示されることを覚えておいてください。」