勉強会のレポート(メモ)です。
参加したのはこちら、「Unityでパフォーマンスの良いUIを作る為のTips 」
techxgamecollege.connpass.comハッシュタグ : #techxgamecollege
スポンサー様
主催は株式会社テクロスです。
東京で新規事業を開発予定です。興味があればどうぞ。
TECHxGAME COLLEGさんではいろいろな勉強会が開催されており、
過去にはUnityのアニメーションについての勉強会もありました。
Unityでパフォーマンスの良いUIを作る為のTips
スライド
話す内容
・UIのおさらい
・パフォーマンスのために注意すること
なんとなく使うとなんとなくしか性能がでません
・バッチング
・フィルレート
・リビルド(ボトルネックになりがち)
その他
UnityUI
・4.6で登場しました(それまではNGUIが主流だった)
・NGUIの人を雇って作りました
・通称UGUI
さまざまな機能があります
・レイアウト
アンカーとポジションを使ったもの
画面解像度の適応に便利
・自動レイアウト
レイアウトが破綻しないようにしてくれる
難しいです
・ビジュアル表現
シェーダーをぶち込む
・インタラクション
ボタンを押したら何かする とか(イベント)
なんとなくそれなりのものが作れてしまうシステムなんですが。
パフォーマンスは勝手に最適化してくれるわけではありません。
柔軟性とパフォーマンスのバランスが大事です。
uGUIはパフォーマンスよりも柔軟性に寄っています。
ではパフォーマンスに寄せるにはどうするか
バッチング
描画処理は後ろからテクスチャをぺたぺた張っていきます。
テクスチャにマテリアルとポリゴンをくっつけて描画するが、これを複数やるのは無駄が多い。
なのでuGUIではCanvasにまとめて一回で描画するシステムがあります。
これがバッチングです。
ルール
・同じCanvas上でなければなりません
・一括描画できるのは同じテクスチャのみ。
問題はバッチングが途切れることがあります。
図では、アーチャー、ライダー、アーチャーで描画されます。
このように別のテクスチャがあるとバッチングが解けてしまいます。
ちなみにtsubakiさんはランサー推しです
・マテリアルが違う場合も途切れます
・座標のZ軸が同じでないとだめ
タイトル画面などで、導入のUIを手前に置くとかだとバッチングが途切れてしまいます。
しかし注意しても途切れることもあります。
そういう時は途切れる部分を探しましょう。
UI Profilerで確認しましょう
バッチングの有無とどうやってバッチングされているかを見ることができます。
バッチングされているUIを選択するとシーンビューでバッチングされてるものがまとめて確認できます。
バッチできない理由も表示してくれます(テクスチャが違うなど)
対策としてはアトラスの機能で1枚でまとめるといいでしょう。
テクスチャが同じなので、まとめて表示できます。
ただ問題があります。
画像に変なものが混じります、これはアトラスがみっちりと詰め込んであるからです。
unityは基本矩形で取るので、タイトパッキングを有効にすることで余裕をもって配置することができます。
これは最近できるようになったものです。(Unity2018.3から)
今は矩形だけでなくメッシュも使えるようになりました、ポリゴンで描画します。
バッチング対策
・条件を満たす
・テクスチャをパックするマスクの使用は抑える(バッチングが途切れます)
フィルレート
unityはポリゴンに色をぬって表現していきます。
1ピクセルずつ塗っていきます 基本的には透明も含めて塗っていきます。
ここで考えることがあります、透明も塗られるということです。
たくさんUIがあって重なっている時は、透明の所も複数回塗られていきます。
パット見わかりませんが重ね塗りされています。
これをオーバードローと言います。
どうやってこれを確認するか、UI Profilerを使いましょう。
色が赤くなるとヤバイところ。問題はこれが広い場合、画面全体が赤いとまずいです。
対策
・不要なUI、見えてないとこを削りましょう
・重なる部分を削る
・UIには真ん中をくり抜く機能があります(Fill Center)
・オーバードローしても問題ない軽量なシェーダーを使う(マスクを抜く 頂点カラーを抜くなど)
ただし、asset bundleを使って配信した場合、依存関係はしっかりね。
現状Unityでシェーダーの負荷を確認することはできません。
各デバイスのプロファイラーで確認しましょう。
XCodeならできます。
UIのリビルド(ジオメトリ)
大事な部分です、ぶっちゃけ前の二つはいらない。
基本的にunityはまとめて描画します。
Canvasが作ったバッファは使いまわすことができるので、ほとんど負荷がかかりません。
ただ使いまわすということは同じデータしか使えないということ。
バッファのデータ(マテリアルやテクスチャ)が変わった場合、再構築が必要になります。
これをリビルドといいます。
ただしバッファの再構築は即時されるわけではない。
更新される条件
・UIのenable/disable
・マテリアルの変化
・RectTransformのサイズの変化
・Transformの親の変化
ここで問題があります、1つ変わるとCanvasのすべてのUIを再構築する必要があります。
たとえ1つでも変わっただけでも全部作り直しです。
ただ幸いなことにメインスレッドには負荷はそれほどでもない。
ただし、Canvasのサイズによってはジオメトリの再構築に時間がかかります。
バッチング時のソートに時間もかかることも(一箇所につまってる場合など)
これはどうやって確認するか、UI Profilerでやりましょう。
リビルドが走るといっぱい山が起きます。
Profilerでも紫色のがびょーんと伸びているとやべーです。
リビルドが起こるのはいいが、だれが起こしたかが重要です。
CPU Profilerでリビルドの発行をしてるやつを探します。
UIは基本動くもの
じゃあどうやるか、手っ取り早いのはCanvasを分割すること。
キャンバスの中にキャンバスを入れることもできます。サブキャンバスといいます。
これをやると影響範囲を減らすことができます。
動かすCanvasと動かないCanvasで分けるといいでしょう。
こうすることで動かすCanvasのみリビルドが走ります。
Canvas間ではバッチングが起こらないので、ドローコールはどうしても増えてしまいます。
もういっこあります。
動くUIはImageのSprite Meshは避けましょう。
バッチングをするという観点からみるとコストが高い。
親UIを動かすとバッチングが途切れます、大きな問題です。
大量のimageを持つ親をうごかすとがくがくになる、
これもUI Profilerで確認しましょう。
これは対策がないです
要素を減らしたり、要素をプーリングして最小限のUIにしたりなど。
ScreenSpaceCameraも同じで動かさないようにした方がよいです。
UIのリビルド LayoutGroup
こっちもリビルドを起こします。
まずおさらい、レイアウトグループはILayoutGroupが整列してくれます。
この機能を使うと、要素が増えたり、大きさが変わっても追従してくれます
柔軟性が高い代わりにパフォーマンスが。。。
その前にDirty Systemについて
汚れたら変更されたと認識して再構築するシステム。
サイズが変わると、Dirtyが入り、LayoutGrouoにリビルドを申請し調整してくれる。
Layoutの中にLayoutがあった場合、親の親の親・・・に通知といいた風に調整してくれます。
問題の話
これは賢い機能だが、どうやっているのか。
GetComponentsでやっています、親のコンポーネントを全部もってきてLayoutGroupがあれば・・・といった風にやっていく。
たくさんあればあるほどやばい・
当然、親から子へ戻るときもGetComponents。
GetConmpnentはやりたくないと思いますが、実はそれほどでもない。
致命的なのは、DirtyのUIが一つでも混じることで、毎フレームUIのリビルドが走ること。
これは一番問題になります。
Mac book proでもリビルドに12secかかるほど。
確認方法はUI Profiler。
レイアウトのリビルドはメインスレッドで動きます。
描画のとこであまり負荷がかかっていない場合はレイアウトを疑いましょう。
対策
・RectTransformに関係するものは操作しない。
タッチしただけでDirtyが走ります。
テクスチャの変化でも走ります。
・UI ElementやLayoutPropertyなどは操作しない
・MecanimのステートマシンでAnimationClipを入れない
再生していないAnimationClipがあったとして、それがRectTransformにアクセスしただけでリビルドが起こります。
SImpleAnimationなら多分OK。Legacyなども。
最もいいのはLayoutGrouopを使わないことです。
その他
・RaycastTargetは外しましょう 毎フレーム負荷がかかります。
・World SpaceCameraのEvent Cameraにはカメラを登録しておきましょう。
実はFindTag(MainCamera)が呼ばれます。
・PixelPerfect設定は動かないカメラのみ設定しましょう。
・テクスチャは画面解像度に合ったものを使いましょう、可能な限り圧縮しよう。
・UI Profilerはエディタのみ、実機のプロファイラでは動きます。
・XCodeなどのプロファイラは便利です
・負荷を確認するのならビルドしたほうがいい
PC向けでもいい 余計なノイズを減らすことができます。
Inspectorにもガンガン更新処理がかかっています。
まとめ
ポイント
・基本動かさない、動かすなら最低限に。
・ドローコールとフィルレート、バッチングで良いバランスを。
ドローコールを極限まで減らすと他のところでしわ寄せがある場合も
・気を抜くな!だれも信用するな!プロファイラーを手放すな!
UI Elementというのが今後きます。
もうちょっとお待ちください。
質問
Q : 動くものと動かないのCanvasはどうわける?
A : 同時に動くもので分けましょう
Q : UIは軽視されがちだが、いつからプロファイリングに力を入れるべき?
A : テクスチャが固まってきたらやった方がいいですが、基本的に早めにやった方がいい
結構開発が進んだ時は?
基本的にフィルレート、UIリビルドです。
レイアウトの再構築を目の敵にするような感じでやるとよい。
Q : Canvasの扱い
描画をしない場合はいろいろ方法がありますが、どういうのがいいの?
A : GameObjectはOffにすると、その下全部がオフになり、バッファも消えるのでよくない。
CanvasのEnableとかおすすめされるが、CanvasGroupを0にしてもよい。
ただしcanvasの大きさ次第。
でかいCanvasはCanvasGroupで、小さいCanvasはEnableでやった方がいいがよい。
この辺はケースバイケース、プロファイルで見ましょう。
タイムライン
@tsubaki_t1 来たぁ~😻 #techxgamecollege #Unity pic.twitter.com/69P5brUhRj
— prototechno (@prototechno) December 12, 2018
バッチングの条件
— mao (@TEST_H_) December 12, 2018
- 同じCanvasに所属
- 同じMaterialを使用
- 同じTextureを参照
- 座標のZ値が同じ
- 同じマスクでクリップされていること(RectMask2Dの場合)#techxgamecollege
Unity 2018.3から矩形でなくポリゴンで! #techxgamecollege pic.twitter.com/nowX8aoG9I
— prototechno (@prototechno) December 12, 2018
フィルレート
— じぬ (@reximol_tech) December 12, 2018
* たくさんの UI がある場合、複数回塗られる箇所が生まれてしまう
* 透明で何重にも塗る、とかありうる(オーバードロー)
* 調べ方:UI Profiler Composite Overdraw
* 重複箇所がヒートマップみたいに見られる #techxgamecollege
UIのリビルドについて(大事)
— mao (@TEST_H_) December 12, 2018
Canvasに詰められたバッファ(頂点)は使い回される。→おかげで大量にUIを置いた場合でも負荷が掛からない。
但しバッファに変化があれば再構築が必要。(リビルド)#techxgamecollege
UI自体は動かしたい時がある。
— mao (@TEST_H_) December 12, 2018
→ Canvasを分割。SubCanvasで対応可能。(Canvasをネストする)
例えば動くCanvasと動かないCanvasで分けるとか。→動く要素は前者に、動かない要素は後者に入れる。そうするとSetDirtyは前者のみに掛かるので後者には影響が及ばない#techxgamecollege
Raycast Targetは可能な限り外す。
— mao (@TEST_H_) December 12, 2018
(寧ろデフォルトで外してくれてもいいのよ...)#techxgamecollege
Raycast Targetのやつ、Editor拡張で作ってみようかな。。
— mao (@TEST_H_) December 12, 2018
※置いたばかりのやつは自動でチェック外すやつ#techxgamecollege
今日は内容もメンツも濃くて楽しめました! #techxgamecollege
— prototechno (@prototechno) December 12, 2018