8.1K Views
September 10, 19
スライド概要
2019/9/5に開催されたCEDEC2019の講演スライドです。
講師:黒河優介(ユニティ・テクノロジーズ・ジャパン合同会社)
Unityのイベント資料はこちらから:
https://www.slideshare.net/UnityTechnologiesJapan/clipboards
リアルタイム3Dコンテンツを制作・運用するための世界的にリードするプラットフォームである「Unity」の日本国内における販売、サポート、コミュニティ活動、研究開発、教育支援を行っています。ゲーム開発者からアーティスト、建築家、自動車デザイナー、映画製作者など、さまざまなクリエイターがUnityを使い想像力を発揮しています。
Unity2018/2019における 最適化事情について
自己紹介 黒河 優介 ユニティ・テクノロジーズ・ジャパン合同会社 エンタープライズコンサルティング デベロッパーリレーションマネージャー・エンジニア
本講演の内容について • Unity Profiler関連のアップデート情報 • 既存機能でのパフォーマンス向上が見込める変更 • パフォーマンス向上に影響ありそうな新機能
Unity Profiler関連のアップデート情報 • Profilerのアップデート • Profiling関連のPackage Update • Profile Analyzer • Profiler Reader ※下記の 2018年度CEDEC講演で触れた内容については端折ります。 https://www.slideshare.net/UnityTechnologiesJapan/unity-111054310
Profilerのアップデート • 2019.3から、300フレーム制限→2000フレームまで • 2019.3から、HierarchyでMain以外のThread対応 • 2019.3から、IL2CPPでもDeepProfilingサポート • 2018.3から追加されたAPI「Profiler.SetAreaEnable」 • 2019.1から追加されたAPI「FrameMetaData」 • 2019.3でCreateGPUProgram時にShaderのkeywordも取 れるように
300フレーム制限→2000フレームまで Preferenceから ProfilerWindowのFrameCount を最大2000まで変更することが可能に。 ※Editor側のバッファサイズを変更する
2000フレームでも足りない場合は… • 300フレーム毎に保存するEditor拡張 https://github.com/wotakuro/UnityProfilerIntervalSave • 実機上で保存したログファイルを分割して読み込む Editor拡張 https://github.com/wotakuro/ProfilerBinarylogSplit ※実機上で保存したログはストレージの許す限り保存されているが ProfilerWindowでの読み出し側が対応していなかったため
Hierarchy ViewのThread対応(2019.3~) 2019.2以前は、MainThrea以外の内容を確認するた めにTimeline表示に切り替える必要があった
Hierarchy ViewのThread対応(2019.3~)
HierarchyでのThread対応(2019.3~) これまでは、リスト表示するHierarchyでは MainThreadしか閲覧できず、別スレッドの処 理を見たい場合はTimeline表示にする必要が あったが、2019.3からは閲覧できるように
Profiler.SetAreaEnable (2018.3~) 上記呼び出しで、実機上でProfilerの ログファイルを書き出しが可能
Profiler.SetAreaEnable (2018.3~) 実機上で保存したログから読み込んだ際、 Memory・Renderingの項目が空の場合は、こ のAPIを叩く必要がある
Profiler.SetAreaEnable (2018.3~) 項目別に有効/無効をセットできるAPI 実機上で書き出したログでCPU以外の項目がデータが 取得できなかった場合は、このAPIを利用して項目を 有効にする必要がある ※ただし CPUを無効にするとProfilerログそのものが 無効になる
実機のDeepProfilingサポート(2019.3) ビルド時のオプションに Deep Profiling Supportが追加
実機のDeepProfilingサポート(2019.3) https://forum.unity.com/threads/remote-deep-profile-can-it-be-activated.77801/
FrameMetaData (2019.1~) Profilerで、実行しているフレームに紐づく情報を 別途バイナリ形式で保存が出来るように
FrameMetaData (2019.1~) • マニュアルのサンプルでは、Textureデータを埋め込ん でいる https://docs.unity3d.com/ScriptReference/Profiling.HierarchyFra meDataView.GetFrameMetaData.html • 他にもモバイル端末での温度等を取得してProfilerに載 せる等を行えば、サーマルスロットリングで処理落ち したかどうか等を追う事が可能になる
CreateGPUProgram (2019.3~) 点光源がオブジェクトの範囲内に来た等で ShaderVariantが切り替わった時に、実機上でShaderコ ンパイルが走りProfiler上では「CreateGPUProgram」 という形でスパイクが表れる
CreateGPUProgram (2019.3~) 2019.2以前ではShader名のみしか載らなかったのが、 ShaderのPass名、Keywordも載るようになった
CreateGPUProgram の対策 Shader.CreateGPUProgramのスパイクを避けるためには、 ShaderVariantCollectionを作成し、ロード時のタイミング にShaderVariantCollection.Warmup を呼び出しておく
Profilerそのものではなく Profilingに関するPackageアップデート
Profile Analyzer
Profile Analyzer • ProfilerWindowからUnity APIを使ってデータを引っ 張って来て整理した状況を可視化するツール • Unity 2019.1からPackageManagerに入った • ソースをPackageManagerの管理下よりコピーすれば、 Unity 5.6でも動作可能(※0.4.0-preview時点では)
Profile Analyzer(蛇足) ProfileAnalyzer.csに記述されている部分が ProfilerWindowからデータを抜く部分
Profiler Reader Profilerのログファイルを直接読んで、サマリーのcsvを 作成するEditor拡張 ※直接ログを読むので300/2000フレームの壁はない
Profiler Reader GC Allocが発生した箇所だけ、Sample名と確保量を 抜き出したcsvなどを書き出せる
Profiler Reader Sample一覧を抜き出してcall数や平均呼び出し時間、 1フレーム当たりの時間等をcsvに書き出し
Profiler Reader • 現在 UnityJapanのgithub上で公開 https://github.com/unity3d-jp/ProfilerReader • CUIで ログファイル→csv変換を呼び出す事も考慮 • ユーザー側でも独自にcsvを追加することが可能 • UnityEditorのバージョンアップごとにログファイルの フォーマットがバージョンアップされるので、 ProfilerReaderも都度バージョンアップして対応中
既存機能のアップデートで最適化された所
既存機能への最適化 Unityではパフォーマンス測定用の テスト及びレポートする環境を 作って既存機能のパフォーマンス についても考慮しながらアップ デートを行っている https://blogs.unity3d.com/2018/09/25/performance-benchmarking-in-unity-how-to-get-started/
既存機能への最適化 https://blogs.unity3d.com/2018/09/25/performance-benchmarking-in-unity-how-to-get-started/
既存機能のアップデート • AnimatorのGameObject非アクティブ化時のリセット対策 • GPU Instancing対応 • TerrainのGPU Instancing対応 • ParticleSystemのGPU Instancing対応 • AndroidでのWorker Threadの調整 • PhysXアップデート
Animatorの非アクティブ化時のリセット対策 • 「Animator.keepAnimatorControllerStateOnDisable」 APIが2018.1~追加 • Trueにすることで、GameObjectのEnable/DisableでAnimatorの ステートを保持し続ける事が可能に • Falseの場合、GameObjectのDisable時にAnimatorの内部バッ ファをクリアしてしまいステート情報が残らない
GPU Instancingについて • 複数の「同一Mesh、Material」のオブジェクトを少な いCPUオーバーヘッドで描画する機能 • DynamicBatchingと違い、CPUでMesh結合処理をしな いで済むのでその分軽い • OpenGLES 2.0、一部のOpenGLES3.0(Adrenoの一部)端 末以外では動作可能 • SystemInfo.supportsInstancing で対応しているか確認 出来る
TerrainのGPU Instancing対応 2018.3以降で設定から有効にする ことでInstancing描画をするように https://blogs.unity3d.com/jp/2018/10/10/2018-3-terrain-update-getting-started/
ParticleSystemのGPU Instancing対応 2018.1以降でParticleSystemの RenderModeがMesh時のみ、 Instancing描画の設定項目が追加
ParticleSystemのGPU Instancing対応 Instancingがオフの場合、ParticleSystem内部ではMeshを 結合する処理があり、負荷が大きい。 InstancingオンにすることでMesh結合する処理をせず、 Instancingで描画するので頂点結合の負荷が下がる
ParticleSystemのGPU Instancing対応 ParticleSystemでの頂点結合 処理はJobで行うため WorkerThread側に出ている
ParticleSystemのGPU Instancing対応 Instancing機能を使うためにはShader側も対応が必要 独自のShaderでParticleSystemのGPU Instancing対応を行 う方法は下記マニュアルを参照 https://docs.unity3d.com/ja/current/Manual/PartSysInsta ncing.html
ParticleSystemのGPU Instancing対応 通常のRendererとParticleSystemのInstancingは Shader側のKeywordが異なる。 MaterialのInspectorに出てくるEnable GPU Instancingの項目とParticleSystemのInstancingは 無関係に動作する
AndroidでのWorker Threadの調整 • Androidで5.6 -> 2017.4でAnimator等のテストでハイエ ンド寄りの端末でパフォーマンス劣化があった • モバイル開発チームで、Android SystraceやUnity Profilerを行った • WorkerThreadを作成する際にCPUのLittele Coreの数 を考慮に入れるように処理を変更 • RenderThreadの優先度も変更
PhysXアップデート • Unityの物理演算はPhysXを利用 • UnityのバージョンとPhysXのバージョンは下記の対応 • Unity 5.0~Unity 2018.2 -> PhysX 3.3.3 • Unity 2018.3~Unity 2019.2 -> PhysX 3.4.2 • Unity 2019.3~ -> PhysX 4.1
PhysXアップデート • PhysXのアップデートにより、パフォーマンスの向上 も得られる • その代わりに細かい挙動も変わるので、ゲームの大 部分が物理に依存していた場合にUnityのバージョン アップの障壁となる可能性がある • CharacterControllerもPhysX機能をWrapperしている だけ
PhysXアップデート • OnCollisionEnter(Collision )等のPhysicsのコールバック で引数があった場合には、呼び出しごとにGC Allocが 発生していた • 2018.3からPhysics.reuseCollisionCallbacksのフラグに よって、GC Allocが発生しない コリジョン時のコール バックが可能になった
[TIPS] PhysX 2017.2から追加された AutoSyncTransformを 外すことでパフォーマンス改善が見込めるケースが ある
[TIPS] PhysX
[TIPS] PhysX After Before
[TIPS] PhysX Physics.Raycast はPhysics側で行う処理。 Physics.Raycast の間で、Transform更新があった場合に Physics側 にSyncすることで、直前に行われたTransform更新を正しく判定処 理することが出来るが、その処理は重い
新機能(描画系) • Dynamic Resolution • Stripping Scriptable Shader Variants • LWRP • SRP Batcher
Dynamic Resolution • GPU負荷が高いときに、解像度を下げる事でフレーム レートを担保する技術 • 以下の二つの組み合わせで実現される • GPU負荷をランタイムで計測するFrameTimingManager • スケーラブルなバッファを管理するScalableBufferManager
Dynamic Resolution • 実装されたタイミングがプラットフォーム毎に異なる • 2018.3のタイミングでは下記をサポート • Xbox One, PS4, Nintendo Switch, iOS, mac, tvOS (Metal only), Android (Vulkan only), Windows Standalone ,UWP (DirectX 12 only).
Dynamic Resolution • FrameTimingによって取得してきたGPU負荷を、 ScalableBufferManagerを使い解像度変更を行う部分は アプリケーション毎にC#で制御する • https://docs.unity3d.com/Manual/DynamicResolution.html • ScalableBufferManagerで管理されて解像度変更を行わ れるかどうかはCamera/RenderTexture毎に設定が可能
Dynamic Resolutionの導入 適応範囲がRendering解像度のみなので、既に進行中の プロジェクトでも比較的導入しやすい。 ただ OpenGLESは未対応なので、モバイルでは片手落ち
Stripping Scriptable Shader Variants • Unityではビルド時に余計なShader Variantを自動的に 判別して削っていた • 2018.2からアプリケーション開発者側でも判別して削 ることが可能になった https://blogs.unity3d.com/jp/2018/05/14/stripping-scriptable-shader-variants/
Stripping Scriptable Shader Variants Shader SubShader Pass Shader情報と Shader Program
Stripping Scriptable Shader Variants Shader SubShader Pass Shader情報と Shader Program Shaderのステージ(vertex/fragment)、キーワード毎に実体がある。 2018.2からは、ビルドに含むか含まないかをC#のコールバックで 制御できるようになる
Stripping Scriptable Shader Variants
Stripping Scriptable Shader Variants IPreprocessShadersインタフェースを継承して、 OnProcessShaderの IList<ShaderCompilerData> を 操作して、どのVariantをビルドに含めるか選択する
Stripping Scriptable Shader Variants Shaderのメモリ使用量を多かった 場合に検討
Stripping Scriptable Shader Variantsの導入 プロジェクトが利用しているShader Variantsを把握して いる、することが可能な環境であるなら、既存プロジェ クトへの適応はしやすい
LWRP • Scriptable Render Pipelineの実装の内の一つ • 2019.3からは Universal RPとなる • 標準Shaderが、モバイルでも考慮されて作られている • 描画パイプラインのカスタマイズの幅が大きい • ただカメラコールバックが使えなくなるなどポストエ フェクト系は作りなおしが必要になる
LWRP(Universal RP) の導入 Shaderへの対応が必要であったり、Material・ PostEffectの作り直しが必要であり、既存プロジェクト への導入はコストが高い Unityのグラフィック開発の多くのリソースは、既に Universal RPに移っている。これからの新しいプロジェ クトの場合はUniversal RPの検討をオススメ
SRP Batcher • Scriptable Render Pipeline使用状況でかつ、SRP Batcherに対応したShaderでのみ有効 • Materialのセットアップを描画の最初の時に出来る限り 行い、その後のMaterial切り替えを出来る限り抑える • GPU Instancingとの共存は出来ない • Platformによりけりだが、Instancingより速いケース が多い https://blogs.unity3d.com/jp/2019/02/28/srp-batcher-speed-up-your-rendering/
SRP Batcher 色々なデータを先にCBufferに 積んでおいて offset切り替えして 描画する仕組み
SRP Batcherの導入 SRP対応が必要なだけでなく、hlslでShaderの記述の必 要があり、CBufferの定義もUnityに沿った形で行う必要 があり、既存プロジェクトへの導入はコストが高い
新機能 • PlayerMainLoop • GC制御用のAPI • Incremental GC • DOTS • NativeContainer / C# JobSystem / Burst / ECS • Mesh API v2
PlayerMainLoop • Unity 2018.1から利用可能 • PlayerLoopから余計な処理を削除したり、処理の間に 好きな処理を入れることが可能 • たとえばPhysXを使わないアプリなら、PhysX関連の 処理そのものをループから消すことが可能
PlayerLoopについて MonoBehaviour Updateを処理 Animatorの処理 MonoBehaviour LateUpdateを処理 Etc....
PlayerLoopについて MonoBehaviour Updateを処理 Animatorの処理 MonoBehaviour LateUpdateを処理 LateUpdateは全くないので、この部分の処理を 全て省くという事が出来たり… Etc....
PlayerLoopについて MonoBehaviour Updateを処理 Animatorの処理 MonoBehaviour LateUpdateを処理 Animatorの処理の前後に特定のC#スクリプトを 差し込むこともできます。 Etc....
PlayerLoopについて PlayerLoopで設定できるステップはかな り種類が多いです。 どんな種類があるかはProfielrのトップ 階層を見ると何となくわかります。
PlayerMainLoop • Unity 2019.3から、Renderingのみをスキップ制御する API「OnDemandRendering.renderFrameInterval」が追加 • モバイル環境で、ロード画面などの画面更新がない時、 バッテリー消費を抑えることが可能
PlayerMainLoopの導入 Physicsを使わないなどの場合は、既存プロジェクトへ の対応は比較的容易。ただ対応しても見込める効果は薄 い
GC制御用のAPI • Unity 2018.3から、GCを制御するAPI “GarbageCollector” が追加された • GCを一定の間発生させなくするという制御が可能に https://docs.unity3d.com/ScriptReference/Scripting.GarbageCollector.html
GC制御用APIの導入 元々GC Allocが少ない所に対して呼び出す等、GC起こさ ないための最後の砦として有効に使う事で、既存プロ ジェクトへの対応もしやすい
Incremental GC • これまでUnityのGCは、GCが走り始めると作業が完了す るまで止まらず、長い処理スパイクになっていた • Incremental GCでは、GC処理を毎フレーム少しずつ走ら せる事で大きなスパイクを回避する • Unity 2019.1ではExperimentalとして実装、2020辺りから IncrementalGCの方がデフォルトになる https://blogs.unity3d.com/jp/2018/11/26/feature-preview-incremental-garbage-collection/
Incremental GC
Incremental GCの導入 PlayerSettingsへの対応のみなので、対応そのものは容 易。 ただメモリ周りの挙動が変わるので、メモリがカツカツ なプロジェクトの場合対応は慎重に
DOTS • Data Oriented Technology Stack • NativeContainer,C# JobSystem,Burst,ECSなどのデー タ志向型のテクノロジーを総称したもの
NativeContainer • Unity 2018.1から利用可能 • GCとは関係なく確保できるメモリ • C,C++のように new / Disposeを自前で管理
C# JobSystem • Unity 2018.1から利用可能 • Unityが確保したJob用のWorkerThreadを利用して処理 の高速化を図る • アプリごとのC#処理もWorkerThreadで出来るが、 ManagedHeapにアクセスできない等の制約あり • 各機能が徐々にC# JobSystem向けのAPIを提供 • ParticleSystem,Physics,Navimesh等
[TIPS] IJobParallelForTransform • 複数のTransformのrotation/positionを並行して操作す るためのAPI • 同じRootオブジェクトを持つTransformに対して操作 が行われた場合には、その場でSync処理が走るように なっている(※2018.4にて確認)
[TIPS] IJobParallelForTransform Chara 0001~0003は同じ Rootオブジェクト 「Characters」以下の子 供になっている
[TIPS] IJobParallelForTransform Chara0003以下にあるTransformをJob発行して 弄っている最中に、 Chara0002のTransformへア クセスしたときには、同じRootを持つTransform へのアクセスがあったと見なし、発行したJob完 了を待ってから、Chara0002へのTransform処理 が行われる
C# JobSystemの導入 コードを書き換える必要があり。 全てをJobSystemで書くことは出来ないので、出来ると ころから対応していく。 複数キャラクターの揺れモノなど、独立性をもって動い ていて負荷が高いものについては効果が出やすい
Burst • Unity のPackageManagerより配布 • 既にpreview外れている • ILコードより、もっと効率的なコードを書き出す • 関数単位で、適応するか選択可能 • 限定された機能のみのC#を高速化する • 新しいMathライブラリを使う必要あり https://docs.unity3d.com/Packages/[email protected]/manual/index.html
Burst の導入 関数単位での適用になるので、部分的に始めていくこと が可能。 JobSystemで対応した部分に対してBurstを適用してみて 処理が速くなるか見てみる等
ECS • データの連続性を考慮した新しいオブジェクト制御シ ステム • メモリ上で処理したときに効率が良く処理できる • ロードも連続性を考慮しているので速い • プログラミングの書き方がMonoBehaviourと大きく異 なる
ECS • まだ previewという形で PackageManagerから配布 • 2018/2019という時点では、まだまだ発展途上 • ECSは UnsafeなC#で書かれている • Burstで高速化されることが前提にある
ECS の導入 まだまだ previewなので、チャレンジャー向け。 参照関係を解決して処理などはまだ書きにくい。 オブジェクトの独立性が高くて数が多い背景のオブジェ クト辺りからECSを試してみる
DOTS相関図 ECS ECSはC#JobSystemで 並行処理を行う C# JobSystem NativeContainer Burst
DOTS相関図 ECS C# JobSystem Burst C#JobSystemでは Managed Heap 使えないので、利用 NativeContainer
DOTS相関図 ECS C# JobSystem NativeContainer 制約があるC#コードで記述 されているのでBurstで最適 コードになることを期待 Burst
Mesh API v2 • 2019.3にて、ランタイムでの動的なMesh生成のための APIが更新 • C# のGC Allocしないで、動的Meshの生成が可能
まとめ • Unity Profilerもアップデートしている • 既存機能も変更してパフォーマンス改善している • 新機能をうまく取り込んでパフォーマンス改善!