Slide 1

Slide 1 text

アニメーションを最深まで理解してパ フォーマンスを向上させる 
 みね


Slide 2

Slide 2 text

株式会社サイバーエージェント 
 SGEマンガ事業部 モバイルエンジニア
 みね
 @mine2424 @nihon_kaizou 2024年度新卒入社
 大学1年から Flutter によるアプリ開発を開始 
 
 # F1 # 麻雀 # 海外旅行 (9割カジノ) # 来年ポーカーの世界大会出場します 
 Nishimine Ryota


Slide 3

Slide 3 text

テーマ選定理由と直近の動向 
 Flutter アニメーション入門 
 Deep Dive Animation パフォーマンスの計測と分析 01
 02
 03
 04
 CONTENTS Improve Animation Performance 05


Slide 4

Slide 4 text

01
 テーマ選定理由と直近の動向 


Slide 5

Slide 5 text

テーマ選定理由と直近の動向 
 01
 なぜこのテーマを選定したか 
 ・アニメーションの中身はブラックボックスとして扱いがち 
 
 ・アニメーションに関するパフォーマンス改善知見が多くない 
 
 ・ネイティブじゃないとリッチなアニメーションは実装できない  (と思われがち)


Slide 6

Slide 6 text

テーマ選定理由と直近の動向 
 01
 アニメーション周りの動向 
 ・Impeller の stable 化 ・Lottie や Rive 等の複雑なパスの計算の向上 (by Impeller)
 
 ・Blur のパフォーマンス向上 (by Impeller)
  Blur の再実装により CPU, GPU の使用率が半分程度まで抑えられた 


Slide 7

Slide 7 text

テーマ選定理由と直近の動向 
 01
 Shader Compilation Jank
 ・Shader Compilation Jank とは?  アニメーションが初回起動時に発生するパフォーマンス低下を起こす問題 
 
 ・Skia 時代  コンパイル時に必要な Shader が widget の描画と同時に生成されるため 
 パフォーマンスの低下を引き起こしていた 
 
 ・Impeller 時代  アニメーションビルド時に必要な Shader を生成するため、
 コンパイル中に発生する Shader Jank を防ぐことができる 


Slide 8

Slide 8 text

テーマ選定理由と直近の動向 
 01
 Stencil-then-Cover Approach
 ・課題
  すべての描画パスを CPU で分割し、その後 GPU に送信して描画します。
 しかしこれでは CPU に大きな負荷をかけパフォーマンスが低下する可能性があった。 
 
 ・解決策
  描画したい形をあらかじめ Stencil Buffer に書き込み必要な部分だけ描画することで、 
 無駄な描画を最低限にすることができる。 
 


Slide 9

Slide 9 text

Flutter 3.24 以上にしたら 
 パフォーマンス超絶向上 


Slide 10

Slide 10 text

02
 Flutter アニメーション入門 


Slide 11

Slide 11 text

Flutter アニメーション入門 
 アニメーション Widget の種類
 02
 ・Implicit / Explicit Animation  主に AnimationController を利用して Widget の挙動を操作する ・Low Level Animation
  CustomPainter を利用して Canvas に描画されるものに対してパラメータを操作することで 
 Widget の挙動を操作する
 
 ・Third Party Animation Framework
  Lottie や Rive といったフレームワークによるアニメーション表現を行う 


Slide 12

Slide 12 text

Flutter アニメーション入門 
 アニメーション Widget の種類
 02
 ・Implicit Animation  Widget の property の値が変更された際にアニメーションを行う 
 AnimationController を使用せずに Widget が使える (AnimatedFoo 系)
 ・Explicit Animation
  AnimationController や Tween を用いて詳細にアニメーションの設定を行うことができる 
 (FooTransition 系)


Slide 13

Slide 13 text

Flutter アニメーション入門 
 Implicit Animation
 02
 AnimationController を使わずに
 値を変化させるだけでアニメーションを実現
 ex) AnimatedContainer


Slide 14

Slide 14 text

Flutter アニメーション入門 
 Explicit Animation
 02
 ex) FadeTransition
 ・AnimationController 使用のため Statefulwidget の場合は 
 SingleTickerProviderStateMixin を継承する


Slide 15

Slide 15 text

Flutter アニメーション入門 
 Explicit Animation
 02
 Animation を制御
 Animation の値を管理 


Slide 16

Slide 16 text

Flutter アニメーション入門 
 Explicit Animation
 02
 AnimationController
 の設定を記述 


Slide 17

Slide 17 text

Flutter アニメーション入門 
 Explicit Animation
 02
 Tween は animation の
 開始と終了の値を定義する
 
 animate() で controller と
 紐付けられる
 アニメーションを開始


Slide 18

Slide 18 text

Flutter アニメーション入門 
 02
 ・SingleTickerProviderStateMixin ・AnimationController ・vsync ブラックボックス

Slide 19

Slide 19 text

03
 Deep Dive Animation


Slide 20

Slide 20 text

Animation はどのような仕組みでできてる? 
 👀
 Deep Dive Animation
 03


Slide 21

Slide 21 text

Deep Dive Animation
 03
 動いていますか? 


Slide 22

Slide 22 text

画像を高速で描画しているだけ 
 Deep Dive Animation
 03


Slide 23

Slide 23 text

1 sec に 60 ~ 120 frame が
 描画されている 
 Deep Dive Animation
 03


Slide 24

Slide 24 text

Flutter の Animation は
 どのように実現しているのか? 
 Deep Dive Animation
 03


Slide 25

Slide 25 text

Flutter における Animation の仕組み
 Deep Dive Animation
 03
 ・Animation class  アニメーションにおける現在の値 (value) と AnimationStatus のみを保持する
  Listenable を継承しているので AnimationController 等で value や AnimationStatus の変化を
 検知することができる。


Slide 26

Slide 26 text

Flutter における Animation の仕組み
 Deep Dive Animation
 03
 ・AnimationController  Animation を継承したクラスで自身が持つメソッドで管理している値を制御できる 
  forward(), reverse(), stop() etc …


Slide 27

Slide 27 text

Flutter における Animation の仕組み
 Deep Dive Animation
 03
 ・Ticker
  1 frame ごとに callback を呼ぶ
 
 ・vsync
  frame を画面のリフレッシュ・レートに 
 同期させることで視覚的な歪みを防いでいる。 
 
 


Slide 28

Slide 28 text

Ticker ? vsync ?
 Deep Dive Animation
 03


Slide 29

Slide 29 text

What is Ticker ?
 ・SchedulerBinding について
  アプリの動作をデバイスのディスプレイの frame と同期させる
 ex) 30 FPS = 30 frame / sec
 Deep Dive Animation
 03
 30 FPS 60 FPS 120 FPS Old Phone iPhone 8 iPhone 16 Pro

Slide 30

Slide 30 text

What is Ticker ?
 Deep Dive Animation
 03
 Event SchedulerBinding
 を介して通信
 Flutter Engine

Slide 31

Slide 31 text

What is Ticker ?
 Deep Dive Animation
 03
 Event SchedulerBinding
 を介して通信
 Flutter Engine Ticker SchedulerBinding
 SchedulerBinding を介して
 毎 frame ごとにイベントが送られるが 
 Ticker で Animation を行うかを制御する


Slide 32

Slide 32 text

What is Ticker ?
 Deep Dive Animation
 03
 Event SchedulerBinding
 を介して通信
 Flutter Engine Ticker SchedulerBinding
 SchedulerBinding を介して
 毎 frame ごとにイベントが送られるが 
 Ticker で Animation を行うかを制御する
 Paint

Slide 33

Slide 33 text

Ticker は frame を制御している 
 Deep Dive Animation
 03


Slide 34

Slide 34 text

Flutter における Animation の仕組み
 Deep Dive Animation
 03
 ・Ticker
  1 frame ごとに callback を呼ぶ
 
 ・vsync
  frame を画面のリフレッシュ・レートに 
 同期させることで視覚的な歪みを防いでいる。 
 
 


Slide 35

Slide 35 text

AnimationController .forward()
 Deep Dive Animation
 03


Slide 36

Slide 36 text

AnimationController .forward()
 Deep Dive Animation
 03
 ・_animateToInternal ではアニメーションを行う際の様々な値の制御を行っている 


Slide 37

Slide 37 text

AnimationController .forward()
 Deep Dive Animation
 03
 最大、最小値を元に
 value の値を調整


Slide 38

Slide 38 text

AnimationController .forward()
 Deep Dive Animation
 03
 Ticker 処理を開始する


Slide 39

Slide 39 text

SingleTickerProviderStateMixin
 Deep Dive Animation
 03
 ・対象の Widget tree が有効な場合に単一の Ticker を更新する mixin ・継承先の class で単一の AnimationController を使用する際に使用( vsync: this で使用)


Slide 40

Slide 40 text

SingleTickerProviderStateMixin
 Deep Dive Animation
 03
 ticker を現在の Widget tree 内で 有効にするかのフラグを管理 
 ticker 自身を管理


Slide 41

Slide 41 text

SingleTickerProviderStateMixin
 Deep Dive Animation
 03
 ticker 自身の
 有効フラグを更新する
 Listenable を更新する


Slide 42

Slide 42 text

AnimatedSlide を使用して 
 リッチなUIを実装
 Deep Dive Animation
 03


Slide 43

Slide 43 text

実装内容
 ・StatefulWidget を継承 ・SingleChildScrollView + Column Deep Dive Animation
 03


Slide 44

Slide 44 text

実装内容
 ・FlutterLogo を AnimatedSlide で
 アニメーションさせる ・1000 個の要素を表示
 Deep Dive Animation
 03


Slide 45

Slide 45 text

Deep Dive Animation
 03


Slide 46

Slide 46 text

カクツキが多い 
 パフォーマンス悪いかも? 
 Deep Dive Animation
 03


Slide 47

Slide 47 text

04
 パフォーマンスの計測と分析 


Slide 48

Slide 48 text

パフォーマンスの計測と分析 
 パフォーマンスの計測方法 
 04
 ・DevTools の活用
 ・Flutter Inspector
  UI のレイアウトや widget のプロパティを確認できる 
 ・Performance View
  アプリのパフォーマンスをリアルタイムで監視し、 CPUやメモリの使用状況を分析 
 ・Memory View
  メモリ使用量の時系列グラフ表示や、メモリインスタンスの分析を行う

Slide 49

Slide 49 text

パフォーマンスの計測と分析 
 DevTools の見方
 04
 ・Frame Chart  1 frame = 1つのバーとして扱い、フレームのレンダリング中に発生する 
 それぞれの役割の作業によってバーの色が異なる 


Slide 50

Slide 50 text

パフォーマンスの計測と分析 
 DevTools の見方
 04
 ・UI Thread  描画コマンドを含む軽量なオブジェクトのレイヤーツリーを生成し、 
 デバイス上でレンダリングされるように raster thread に送られる
 (簡単に言えば、CPU の処理に関する Thread)


Slide 51

Slide 51 text

パフォーマンスの計測と分析 
 DevTools の見方
 04
 ・Raster Thread  Skia や Impeller といった Flutter Engine からグラフィックコードを実行するもの。 
 なので UI Thread でのアプリケーションロジックの実行とは別で扱う。 
 (簡単に言えば、GPU の処理に関する Thread)


Slide 52

Slide 52 text

パフォーマンスの計測と分析 
 DevTools の見方
 04
 ・Jank  frame のレンダリングが 16 ms 以上 かかる場合に Jank としてみなされこの色になります 
 : frame 内で Jank が
 発生した箇所
 (60 FPS のデバイスの場合 )

Slide 53

Slide 53 text

パフォーマンスの計測と分析 
 DevTools の見方
 04
 ・Shader Compilation  最初にアプリ内の描画と同時に shader を生成している場合
 : Shader Compilation が
 発生している箇所

Slide 54

Slide 54 text

パフォーマンスの計測と分析 
 計測時の注意点 
 04
 ・パフォーマンス計測時は Profile Mode で実行すること 
 ・実行の違い 
 Debug : JIT (just in time) その場で compile される Profile : AOT (ahead of time) アプリが読み込まれる前にpre-compile されて実行

Slide 55

Slide 55 text

パフォーマンスの計測と分析 
 計測対象
 04
 ・AnimatedSlide で実装 ・1000個のアニメーションする要素を用意 


Slide 56

Slide 56 text

パフォーマンスの計測と分析 
 計測結果
 04


Slide 57

Slide 57 text

パフォーマンスの計測と分析 
 計測結果
 04
 : AnimationSlide の 描画箇所

Slide 58

Slide 58 text

パフォーマンスの計測と分析 
 計測結果
 04
 : AnimationSlide の 描画箇所 重複した処理が続いてしまっている 


Slide 59

Slide 59 text

パフォーマンスの計測と分析 
 計測結果
 04
 ・Paint Time
 196.763 ~ 196.780 s => 0.17s (170 ms)  
 1frame ~ 16 ms 以下に抑えられていない 


Slide 60

Slide 60 text

パフォーマンスの計測と分析 
 計測結果
 04
 ・Rebuild Count
 performance view の rebuild stats で確認
 要素の数と同じ回数 rebuild されている 


Slide 61

Slide 61 text

05
 Improve 
 Animation Performance


Slide 62

Slide 62 text

Improve Animation Performance
 改善策を考える 
 05
 ・一括でアニメーションをまとめられないか   ・Repaint の範囲を抑える 


Slide 63

Slide 63 text

パフォーマンスの計測と分析 
 一括でアニメーション操作 
 04


Slide 64

Slide 64 text

パフォーマンスの計測と分析 
 一括でアニメーション操作 
 04


Slide 65

Slide 65 text

パフォーマンスの計測と分析 
 Repaint の範囲を抑える 
 04
 ・RepaintBoundary  子ウィジェットの描画を他の部分から分離し、特定の部分だけを Repaint させる
 
  同じ Layer を共有する RenderObject は markNeedPaint にて 
 repaint が必要だと判断されるたびに repaint が発生する
 
  同じ Layer を共有する RenderObject : 祖先にある要素, 子の要素どちらも含む


Slide 66

Slide 66 text

パフォーマンスの計測と分析 
 Repaint の範囲を抑える 
 04
 はみ出ている... ref: https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html

Slide 67

Slide 67 text

パフォーマンスの計測と分析 
 Repaint の範囲を抑える 
 04
 はみ出ている... 無駄なペンキ塗りが発生... ref: https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html

Slide 68

Slide 68 text

パフォーマンスの計測と分析 
 Repaint の範囲を抑える 
 04
 はみ出ている... 無駄なペンキ塗りが発生... 事前にテープを貼ろう! ref: https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html

Slide 69

Slide 69 text

パフォーマンスの計測と分析 
 RepaintBoundary
 04
 親 子 Transform.rotate ListView repaint が発生
 RepaintBoundary なしの場合


Slide 70

Slide 70 text

パフォーマンスの計測と分析 
 RepaintBoundary
 04
 親 Transform.rotate ListView repaint が発生
 RepaintBoundary なしの場合
 子 祖先へ repaintが波及


Slide 71

Slide 71 text

パフォーマンスの計測と分析 
 RepaintBoundary
 04
 親 Transform.rotate ListView repaint が発生
 祖先へ repaintが波及
 RepaintBoundary なしの場合
 子 repaint 範囲が広がる


Slide 72

Slide 72 text

パフォーマンスの計測と分析 
 RepaintBoundary
 04
 親 子 Transform.rotate ListView repaint が発生
 RepaintBoundary ありの場合
 RepaintBoundary を付与


Slide 73

Slide 73 text

パフォーマンスの計測と分析 
 RepaintBoundary
 04
 親 子 Transform.rotate ListView repaint が発生
 RepaintBoundary ありの場合
 repaint が祖先まで 
 波及しないようになる 


Slide 74

Slide 74 text

パフォーマンスの計測と分析 
 Repaint の範囲を抑える 
 04


Slide 75

Slide 75 text

パフォーマンスの計測と分析 
 検証結果
 04
 効果はバツグンだ! 
 DevTools の Flutter Inspector


Slide 76

Slide 76 text

パフォーマンスの計測と分析 
 検証結果
 04
 ・計測結果 
 0.673 ~ 0.765 s => 0.08s (80 ms) before 170 ms
 ・Rebuild 回数
 


Slide 77

Slide 77 text

パフォーマンスの計測と分析 
 RepaintBoundary の付与基準 
 04
 Q: metrics の数値が高ければどこにでも付与すれば良いのか?
 RepaintBoundary has the further side-effect of possibly hinting to the engine that it should further optimize animation performance.
 Flutter Engine (GPU) に副作用をもたらす可能性 
 (GPU 内のメモリコストを逼迫させるため) 


Slide 78

Slide 78 text

パフォーマンスの計測と分析 
 RepaintBoundary の付与基準 
 04
 ・Component 化
 ・Builder 系
  AnimatedBuilder, HookBuilder
 
 解決できない場合のみ付与 
 
 ・Rive 等の animation framework
 ・コンポーネント内に
 複数のアニメーションが存在している 
 A : 段階的に付与するか判断する


Slide 79

Slide 79 text

Animation のパフォーマンスで悩んでいる ...
 どうしてもパフォーマンスが良くならない ....
 → Ask The Speaker へ
 自分も知らない事例がたくさんあるのでぜひ一緒に考えましょう! 


Slide 80

Slide 80 text

ご清聴ありがとうございました 
 Thank you!