Anúncio
Anúncio

Mais conteúdo relacionado

Apresentações para você(20)

Similar a Unityでパフォーマンスの良いUIを作る為のTips(20)

Anúncio

Mais de Unity Technologies Japan K.K.(20)

Último(20)

Anúncio

Unityでパフォーマンスの良いUIを作る為のTips

  1. Unityでパフォーマンスの 良いUIを作るためのTips Tatsuhiko Yamamura @ Unity ver 1.1
  2. お品書き • UnityのUIシステムおさらい • パフォーマンスの良いUIの為に注意すべきこと • バッチング • フィルレート • リビルド • その他
  3. Unity UI
  4. Unity UI • Unity UI、通称UGUI • Unity 4.6のタイミングで追加 • オープンソースで開発
 https://bitbucket.org/Unity-Technologies/ui/ • 当時はNGUIというアセット(有料)を殆どの人が使用していた • NGUI開発者が中の人になって一緒に開発
  5. uGUIの機能 • Text • Image • Raw Image • Button • Toggle • Slider • etc • Basic Layout • Layout Group • etc ビジュアル表現 インタラクション レイアウト uGUIには結構いろいろな機能がある
  6. UGUIの機能 • 基本的なレイアウト • 自動レイアウト • ビジュアル表現 • インタラクション
  7. UGUIの機能 • 基本的なレイアウト • 自動レイアウト • ビジュアル表現 • インタラクション
  8. UGUIの機能 • 基本的なレイアウト • 自動レイアウト • ビジュアル表現 • インタラクション https://github.com/mob-sakai/UIEffect
  9. UGUIの機能 • 基本的なレイアウト • 自動レイアウト • ビジュアル表現 • インタラクション
  10. 全自動
 UI最適化ボタン 結構、なんとなくで作れてしまうUIシステム、 ただしパフォーマンスも担保してくれるかというと、微妙にしてくれない。 全自動最適化ボタンは無い。
  11. パフォーマンス柔軟性 柔軟性とパフォーマンスはトレードオフ 実際に標準の機能を使用せず、自作する必要も出てくる
  12. バッチング
  13. 描画処理 サンプル キャラクター サンプル キャラクター サンプル キャラクター 描画は奥から順にスタンプのように貼り付けていくイメージ
  14. 描画する流れ Texture Material (+Shader) Polygon 描画 描画は基本のポリゴンと同じく、マテリアルでポリゴンを塗る方式
  15. 複数繰り返す 描画処理をたくさん繰り返すのは効率が悪い
  16. Canvasに一旦まとめる Canvas マテリアル テクスチャ UIのレイアウト 結合したポリゴン 平面上の要素なので、多少楽をしている(らしい) ※まとめるのはHierarchyではなく実際の描画ベース
  17. まとめて一回で描画(バッチング) まとめたポリゴンを一気に描画すれば、大量に描画しても負荷はソコソコ
  18. 一括描画できるのは
 同じテクスチャのみ 異なるマテリアルの場合、異なるポリゴンを用意して描画(バッチが解ける)
  19. 入れ子のせいで
 バッチングできない スプライトが入れ子になるとバッチング出来なくなる ※Hierarchyではなく描画ベースの入れ子
  20. 入れ子のせいで
 バッチングできない テクスチャが同じでもマテリアルが異なるとバッチング出来ない
  21. バッチが可能な条件 • 同じCanvasに所属している • 同じMaterialを使用している • 同じTextureを参照している • 座標のZ値が同じ位置にあること • 同じマスクでクリップされていること(RectMask2Dの場合) バッチングの為には全ての条件を満たす必要がある
  22. UI Profilerで観察 確認 方法バッチングが解けてるかどうかはUI Profilerで確認 (Unity 2017.1から)
  23. バッチ一覧 バッチで
 まとめた
 UI 描画一覧と、バッチでまとめたUIの一覧を確認できる
  24. バッチ出来ない理由 テクスチャが違う バッチが解ける理由も表示される
  25. Sprite Atlas 対策 バッチをまとめたい場合はSpriteAtlasにまとめる
  26. 参照するテクスチャが
 同じなので、バッチングが出来る Sprite Atlasで
 一つのテクスチャにまとめる UVの異なる矩形ポリゴン 一枚のテクスチャにまとめれば、同じマテリアルで複数のUIを表示出来る
  27. 違うタイプのキャラクターが
 混ざっても1回で描画できている 実際の描画の様子
  28. 元画像 なにか違う… 右はオリジナル、左はSpriteAtlasで取得したスプライト。妙な模様が追加されている
  29. スプライトをミッチリと詰めた為に発生。UIは基本的に矩形なので、ミッチリ詰めると発生
  30. 余裕を持って
 配置する 対策 対策1:Tight Packingを止めて、余裕を持って配置
  31. Imageを矩形ではなく
 ポリゴンで描画する
 (Unity 2018.3から) 対策 対策2:矩形ではなくポリゴンを使用して描画
  32. バッチング対策 • バッチの条件を満たす • テクスチャをパックする • マスクの使用は抑える 対策
  33. フィルレート
  34. ポリゴンに色を塗る際は
 1ピクセルずつ塗っていく ポリゴンを表示し、色を画面(スクリーン)の1ピクセルずつ塗っていく。これをマテリアル単位でGPUで一括で行う
  35. 沢山のUIがある場合 重なって表示している場合に問題になる。
  36. 沢山のUIがある場合 複数回塗られている 透明は「描画しない」ではなく「透明を塗る」なので、重なっている部分が複数回塗られる(オーバードロー)
  37. UI Profiler
 (Composite Overdraw) 確認 方法UI ProfilerのCompsiteOverdrawで確認できる。色が赤に濃いほど負荷が高い
  38. UI Profilerで確認しているの図
  39. オーバードロー対策 • 不要なUIを排除 • 重なる部分を出来るだけ削る • Sprite Meshを使用 • Fill Centerを外して、中央をくり抜く • オーバードローしても問題ないシェーダーを使う sprite mesh使用 通常のquad 対策
  40. • シェーダーの負荷は
 各デバイスのプロファイラーで 確認する • XCodeはいいぞ
  41. UIのリビルド
 (ジオメトリ)
  42. Canvasに一旦まとめる Canvas マテリアル テクスチャ UIのレイアウト バッファ 前述の通り、Canvasは一旦描画するUIのポリゴンをまとめて、バッファを生成。 バッファは基本使い回される
  43. 2300個のImage 殆ど負荷がかからない 単一描画になるので効率よく描画できる。バッチング出来ているなら殆どCPUに負荷がかからない(iPadPro1stで検証)
  44. 変化があれば再構築が必要 Canvas マテリアル テクスチャ UIのレイアウト バッファバッファ 更新 更新 更新 UIを動かすと以前のバッファを使い回せなくなるので、バッファを作り直す必要がある
  45. バッファの再構築は即時ではない Update LateUpdate Post Rate Update SendWillRenderCanvasesSetDirty 変化した内容に応じて Dirtyフラグを付ける UIを再構築 Canvas バッファは即時反映ではなく、変化した物にDirtyフラグをセット、溜めて一括処理する
  46. 再構築の条件 • UIをenable/disable • マテリアルが変化 • RectTransformのサイズが変化 • Transformの親が変化 UI再構築の条件は結構緩く、簡単に再構築が走る
  47. 一つ変わると、同じCanvasの全ての UIを再構築する Set Dirty 同じCanvasが全て
 更新対象に ジオメトリはCanvas単位で管理しているので、一つでも変更があれば同Canvas全てに影響する
  48. このUIのどれかが動けば、背景のUIにリビルドが走る
  49. メインスレッドの
 処理は少ない Canvas内のサイズによってジオメトリの再構築に時間がかかる 配置により
 ソートに時間がかかる ジオメトリの再構築はメインスレッドではない為、即座にフレームが落ちる訳ではない(テキスト…フォントの取得は除く)。ただし
 ジオメトリ生成完了までレンダースレッドが遅延するので、量によっては固まる。また、他のジョブ進行にも影響する。
  50. 確認 方法 UI Profilerが動いている 動いていなければ静か 確認方法 UI Profilerで確認出来る
  51. Profilerでも分かりやすい 確認 方法 通常のプロファイラーでもUGUIRenderingが伸びるので確認できる
  52. CPU Profiler(Hierarchy)で OnDidApplyAnimationPropertiesや OnRectTransformDimensionsChangeで検索 追加表示項目を変更
 Show Related Objects(関連オブジェクトを表示) UI再構築してそうなの一覧が表示される 確認 方法 UIのEventSystemでUIが動いている場合、誰がUIを動かしたのかを確認できる
  53. Canvasを分割 対策 対策1:SubCanvasを作りジオメトリを分割する
  54. Set Dirty Static Canvas Dynamic Canvas 違うCanvasなのでリビルド対象外 Canvasを分割 Canvasを分割することで、UIを動かしてもジオメトリの再構築範囲を限定できる
  55. (Canvasのジョブ完了待ちが
 消えた効果もある) Canvas分割の効果。UIのソートやジオメトリの生成コストが殆ど無くなる
  56. 動くUIはImageのSprite Meshを避ける use sprite mesh : enable 3.22ms use sprite mesh : disable 0.4ms対策 SpriteMeshはポリゴンが多く、ジオメトリ生成のコストが上がる。動かすUIには使わない方が無難
  57. 親UIを動かす element element element element element element element root 親Transformを動かした場合もUIの再構築が走る。 ScreenSpaceCameraを使用している場合、よく起こる
  58. 動かしたタイミングで負荷 確認 方法 UIProfilerでRenderの山ができている場合はコレ
  59. • 要素数を減らす • 要素をプーリングして
 最小限のUIでどうにかする 対策 コレといった対策は無い。 動くCanvasには要素はあまり突っ込まないのが良い
  60. UIのリビルド
 (Layout Group)
  61. RectTransform 子の RectTransform 子のRectTransform 子 の RectTransform 子の RectTransform LayoutGroupは、例えばRectTransformの中に大量のUIがある時、
  62. RectTransform
 ILayoutGroup 子のRectTransform 子のRectTransform 子のRectTransform 子のRectTransform ILayoutGroupで
 子RectTransformの情報を収集し 子オブジェクトを自動的に並べてくれる(レイアウトする)機能
  63. RectTransform
 ILayoutGroup ILayoutSelfController 子のRectTransform 子のRectTransform 子のRectTransform 子のRectTransform ILayoutSelfControllerで
 子RectTransformの情報を元に
 自分のオブジェクトのサイズを変更する 子オブジェクトの情報を元に、親(自身)のサイズを調整することも可能
  64. RectTransform
 ILayoutGroup ILayoutSelfController 子のRectTransform 子のRectTransform 子のRectTransform 子のRectTransform 要素が増えても
 自動的に対応子のRectTransform 子のRectTransform 要素が増えたりサイズが変わっても対応できる、柔軟性を持つ
  65. うまく組み合わせれば、横に伸びて縦に要素を加算するUI…みたいなものも作れる
  66. 特にScroll Viewとかでよく使われる機能
  67. Dirty System Layout 
 Group Element Element Element Element UI LayoutもDirtySystemを使用して実現している
  68. Dirty System Layout
 Group Element Element Child Child DirtyElement UIの変更があればDirty (この例では緑のElementのサイズを変更)
  69. Child Dirty System Layout
 Group Element Element Element リビルド申請 DirtyしたUIから親LayoutGroupへ、レイアウトの再構築を申請
  70. Element Dirty System Layout
 Group Element Element Element 再レイアウト あとで一斉にUIを再レイアウトする
  71. Dirty System Layout
 Group Element Element Element Layout
 Group Layout
 Group Element Element Element リビルド申請 Element 複数のLayoutGroupがある場合は
  72. Dirty System Layout
 Group Element Element Element Layout
 Group Layout
 Group Element Element ElementElement LayoutGroupの親まで到達して、そこから再レイアウト (なので、途中で自身のサイズを変えるContentSizeFitterはLayoutGroupの間に挟むと具合が悪い)
  73. Groupの更新 Layout
 Group Element Element Element Layout
 Group Layout
 Group Element Element ElementElement parent.GetComponents() parent.GetComponents() rect.GetComponents() この親LayoutGroupへの通知は、毎回GetComponentを使用。子のレイアウト変更にもGetComponentを使用。
 地味に負荷になる
  74. DirtyするUIが紛れ込むだけで
 毎フレーム、UIのリビルドが走る 一つでもDirtyするUIが含まれると、毎回Layoutの再構築。 これはメインスレッドで実行され、妙に高い負荷になる
  75. UI Profilerが動いている 動いていなければ静か 確認方法 確認 方法 再レイアウトが起こっている場合、UI ProfilerのLayoutが大きく変化する
  76. メインスレッドで処理されるため、大きな負荷と認識される事が多いです。 再レイアウト処理
  77. • RectTransformに関連するものを操作しない
 (結果は変化しなくとも、セットするだけでリビルドが走る) • Layout Propertyに関連するものを操作しない • UI Element関連を操作しない • Mecanimのステートマシンに、上記の要素にアクセスする
 AnimationClipを入れない
 (Simple Animationなら多分OK) 対策
  78. 対策 もしUIのインタラクティブな機能(ScrollViewとかButtonとか)から呼び出している場合、ProfilerのUI Detailで
 誰が呼び出しているのか確認できる
  79. LayoutGroupを
 可能な限り使わない 対策
  80. その他
  81. TextコンポーネントのTextを変更する際にメインスレッドでフォント取得処理が発生 (※変更しなければ発生しない)
 Textコンポーネント1つなら問題無いが、大量にあると負荷になる 500個のUI.Textコンポーネントのtextを変更 その他 Font.CacheFontForText
  82. その他 • Raycast Targetは可能な限り外す • World Space CameraのEvent Cameraにはカメラを登録する
 (無い場合はFindTag(“MainCamera”)を呼ばれる • Pixel Perfect設定は動かないカメラのみ設定する • テクスチャは画面解像度に合ったものを使う、
 可能な限り圧縮する
  83. その他 • UI Profilerはエディターのみ動作 • XCode等のデバイス専用プロファイラーは
 詳しい情報を収集できて便利 • 負荷を見るならビルドする
 Windows向けでも、多くのノイズを取り除いてくれる
 (実機で確認するのがベターではある)
  84. まとめ
  85. ポイント • 動かさなければ負荷は低い • 動かすなら最低限の影響に抑える • ドローコールとフィルレート、バッチング。 良いバランスを取る •気を抜くな!誰も信用するな! プロファイラーを手放すな!
  86. UI Element(Runtime) Coming Soon
  87. おしまい
Anúncio