ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
レンダリングフェーズでの滞りを紐解き排除する
Appに複雑なビュー階層を実装すると、アニメーションの滞りが発生する可能性があります。レンダリングフェーズでどのようにビューがピクセルに変換させるかを紐解き、レンダリングループのこのパートにおける問題をInstrumentsで解明する方法について確認します。Appの使用時に優れたエクスペリエンスを提供するために、どのようにオフスクリーンパスを排除するか、またXcodeにおける最適化の機会を活用するかについても確認します。
リソース
関連ビデオ
WWDC23
Tech Talks
-
ダウンロード
OSパフォーマンスチームのパトリックです 本日はApp内のrender hitchを 解き明かし取り除く方法を見ていきます
iOSはRender Loopを使ってビューを表示します そしてhitchとはRender Loopが時間内に フレームを完成できないことを言います
Render Loopの概要は― "Explore UI Animation Hitches and The Render Loop"をご覧ください 今回の主題はrender hitch レンダー準備や実行フェーズでの遅延により 発生するhitchです
まずこの2つのレンダーフェーズは何か? 次にrender hitchをInstrumentsや Xcode view debuggerを使って― 発見し選別する方法 最後にレイヤーツリーを最適化し― hitchを修正する方法をいくつか紹介します まずはレンダーフェーズを定義しましょう
コミットフェーズではAppがUIを変更し― 更新されたUIレイヤーツリーを送信します これらの送信をコミットと呼びます render serverは― 全フォアグラウンド・プロセスにおいて コミットをレンダーすることが仕事です render serverでの作業が 1フレームより長くかかると― hitchが起きます App外のプロセスで起きる作業ですが レンダーはAppの代理で行われるので レイヤーツリーのレンダーにかかる時間は 皆さんの責任です render serverには2つのフェーズ レンダー準備とレンダー実行があります レンダー準備フェーズではレイヤーツリーが― GPUが実行する単純な工程のパイプラインに コンパイルされます 数フレームかかるアニメーションも ここで処理されます レンダー実行フェーズでは― GPUがAppのレイヤーの最終画像を描画し 表示可能にします どちらのフェーズでも遅延が起こり得ます
説明のため 例を見てみましょう このフレームのレンダリングを説明します 丸とバーの両方に影があることにご注目ください 後で重要になります まずはrender serverにAppが送信した レイヤーツリーから見ていきましょう render serverがレイヤーごとにステップを 描画コマンドのパイプラインへとコンパイルし GPUに後ろから前へUIを描画させます
ルートノードを始点に― render serverは 階層内の全レイヤーを 兄弟から兄弟へ 親から子へと進めていきます
そして次の実行フェーズでGPUが実行する パイプラインが完成します GPUはこのパイプラインを使い― 各ステップを描画し最終画像を完成させます そのテクスチャが 表示フェーズで画面に表示されます まず青いレイヤーから― 指定の境界に色が描画されます 次に境界内に暗いブルーが描画され 次へ進みます
しかしここで影を描かなければなりません 影の形は次の2つのレイヤーで定義されており GPUには影の形が分かりません でも丸とバーを先に描けば― 影で隠されてしまい正しく描かれません 手詰まりです 続けるには 別のテクスチャにスイッチして― 影の形を調べねばなりません これをoffscreen renderingと呼びます 最終的なテクスチャとは違うところで 描画するからです ここなら丸とバーを描くことができます これで影の形が 画面外のテクスチャに描けました レイヤーを黒く塗りボヤけさせれば― 影の形ができます この画面外のテクスチャをコピーすれば― 影のレイヤーが完成します
次のステップで再び丸と四角を描きます そして最後に上に書かれたテキストを コピーして完成です
レンダーフェーズが両方完了し フレームが表示可能になりました ただ影のレンダーに手間をかけており― レンダーに時間がかかりました これをoffscreen passと呼びます
offscreen passとは GPUがレイヤーを別の場所でレンダーして コピーすることを言います この影の場合は 形を知るためレイヤーを描く必要がありました offscreen passは 積み重なってhitchの原因になり得るので― モニターし避けましょう
offscreen passには 最適化できる4つの主要タイプがあります 影 マスク 角丸四角と視覚効果です 先ほどの例では影をお見せしました 影の場合は レンダラーが十分な情報がないため― まず先に 影の元となるレイヤーが描かれます 2つ目のタイプは― レイヤーにマスキングが必要なものです レンダラーは マスクされたサブツリーをレンダーしますが マスク外のピクセルを 描画しないようにしなくてはなりません そこでサブツリー全体を画面外でレンダーし― マスク内のピクセルのみをコピーして 最終テクスチャに戻します ユーザーが目にしないピクセルを― 大量にレンダーする場合もあります
3つ目はマスキングに関連しています レイヤーの角を丸くする場合です 情報が足りなければ― レンダラーが画面外でビュー全体を描画し 丸めた形をコピーしなければなりません
4つ目は特殊効果です UI Kitは2タイプの特殊効果を提供しています VibrancyとBlurです 適用するには― 特殊効果ビューの下にあるものを 別テクスチャにコピーしなければなりません そこに特殊効果を適用してコピーして戻します これはUIナビゲーションや― UIタブバーなど様々なコントロールに見られます iOSやtvOSやmacOSでもよく見られます
これら4タイプのoffscreenはレンダーを遅らせ hitchの原因となります
以上 レンダーフェーズの詳細と― 原因となるoffscreen passを説明しました 2つ目のトピックに移りましょう Instrumentsでhitchを見つける方法です
Instruments 12では hitchを解析できるように― 新しいテンプレートをリリースします
食事計画Appでhitchの苦情があったので― 調べてみましょう Instrument内でAppをスクロールします
これはanimation hitches templateを使った 食事計画Appの追跡です スクロール中にいくつかhitchがありました ズームインして hitch 16のトラックを見てみましょう
各トラックは先述したRender Loopの各段階に 対応しています 一番上が最も重要なトラックで hitchのインターバルが示されます フレームの準備で超過した時間です
User Eventsには― hitchのフレームと関連した ユーザーイベントが示されます
Commitsには― フレーム中にrender serverに送られた コミットフェーズがすべて示されます これらのトラックの詳細は― "Find and Fix Hitches in the Commit Phase"をご覧ください
次がこの動画の主題です RendersとGPUは render serverによる作業が示されます
Frame Lifetimesはフレームをイベントから 表示まで構成するのにかかった時間が示されます 最後にBuilt-In Displayは― ディスプレイに現れたすべてのフレームと その間のVSYNCが示されます
Frame Lifetimeを Hitch Durationの始まりと比較すれば フレームの構成が終わるはずだった インターバルを視覚化できます このhitchでは2フレームオーバーでした VSYNCを見ると― コミットとレンダーフェーズが 時間超過していると分かります このインターバルは Acceptable Latencyと呼ばれます その後の時間はHitch Durationです
Hitchトラックが選択してあると トラックの下に― hitchの詳しい数値が出ます hitch 16を見ましょう こちらはhitchの長さとレイテンシーです フレームの完成にかけられる時間です
Buffer Countは― hitchが起きた時に render serverが使ったバッファの数です デフォルトは2ですが レンダーしているフレームが遅れて render serverが追い付こうとしている時は3になります ダブルバッファモードでは2フレーム iPhoneの場合は33.34ミリ秒あります レイテンシー列の数値です 必ず上のHitch Durationトラックを 追いましょう バッファ数に関わらず 大事なエリアがハイライトされます
最後にHitch Type hitchのタイプを示し― Appで何を調べるべきかの指針になります ここではExpensive CommitとGPU timeが 示されています 上のトラックにありました RendersとGPUトラックに着目し― 選択して準備と実行フェーズの詳しい情報を アナライザーで見ましょう
重要な列はRender Countです GPUが要したoffscreen passの数が見られます render hitchがあったので― offscreenを見て その原因と修正方法を調べなくてはなりません レイヤーツリーを見るには Xcode view debuggerが一番です そこでデモを見ましょう
view debuggerで 食事計画Appが表示されています 左側にはビューコントローラや ウィンドーや制約やビューがあります Xcode 11.2から レイヤーも表示できるようになりました Editorから 新しいShow Layersをクリックしましょう
いいですね 左のナビゲーターから― ビューをクリックすれば そのレイヤーとサブレイヤーが見られます
レイヤーを選択すると― 新しいLayer Inspectorが表示されます レイヤーの有用なプロパティが示されます そしてこちらに tag views backing layerがあります バックグラウンドの色や透明度 maskToBoundsが有効かなど 様々なプロパティが示されます 何よりoffscreenの数が分かります レイヤーのレンダー時に要した数です その下はoffscreen flagsと呼ばれるもので offscreenの原因を示します 例えばOffscreen Maskというフラグなら 複数のoffscreenを引き起こせます ここでは2つです ただApp全体の各レイヤーを調べて 数を割り出しても― offscreen passを減らすのに 十分な情報にはなりません ビューとレイヤー階層で パフォーマンス最適化の特定と手助けのために Xcode 12では 新しいruntime issue typeを加えました optimization opportunitiesと呼ぶものです
デフォルトで有効ですが Editorメニューの Show Optimization Opportunitiesで選べます optimization opportunitiesは 長年― レンダリングパフォーマンスの最適化をしてきた Appleのパフォーマンスチームが開発したものです レイヤーの見た目に影響を与えない― シンプルかつ有用な変更を提案します ナビゲーターには このように 紫のruntime issue indicatorが表示されます
こちらは星のレイヤーです inspectorではoffscreenが5つとあります インジケーターのハイライトを見ると dynamic shadowsが原因だと分かります runtime issue navigatorで詳細を見ましょう
ここで問題のメッセージが読めます “レイヤーが レンダーが難しい dynamic shadowsを使っている” “可能ならshadowPathを設定” “影を事前にレンダーして画像に入れて レイヤーの下へ”
このタイプのoffscreenは スライドで説明しました レンダラーに十分な情報がなく― 影の形を知るため 画面外でレイヤーを描くのです CALayerでshadowPathプロパティを使えば― レンダラーに手順を示し 5つのoffscreenを取り除くことができます
これはとても有効です コードを見て変更してみましょう
ここが影が必要な星のレイヤーです 提案されたshadowPathは― どんなCGPathも受け取ります すでにあるstar pathを再利用しましょう
これだけで CollectionViewCellで 5つのoffscreen passを取り除けました 大きな修正です
デバッグナビゲーターに戻ると まだ他にも runtime issue indicatorが見られます タグビューでは レイヤーにruntime issueが見られます ナビゲーターを見ると マスクが原因で2つoffscreenがあります このレイヤーは image viewとlabel viewから成ります 赤い背景からものが外れないよう― 我々はマスクレイヤーを加えました runtime tool tipから― 原因はsimple background color maskingだということが分かります
ナビゲーターの最適化テキストを見ます “このレイヤーは simple layerを使っており―” “背景色がマスクに設定されている” “代わりに 同じフレームと角の丸みの container layerをマスクとして使い” “maskToBoundsを有効に” offscreenの原因はレンダラーが マスクレイヤーを先にレンダーするからであり このレイヤーを削除することが提案されています コードで見てみましょう
これはtag viewです マスクレイヤーを作り― 黒の背景と角の丸みを与えます このマスクレイヤーはシンプルです simple layerはマスクにすべきではありません 代わりに 実際のレイヤーで このrequested mask shapeを定義して レンダラーに描画を最適化させます そして角の半径を10に設定して― maskToBoundsをTrueに設定します そしてマスクを削除します
まだoffscreenが1つ残っています サブレイヤーがあるため― ビューが正しくクリップされたか確認するのに maskToBoundsがoffscreen passを行うのです でも今回は― サブレイヤーが tag viewの境界を越えないようにしました だからマスキングが必要ありません maskToBoundsコールを削除しましょう これでoffscreenを2つとも削除できました ここまで3行のコードで― CollectionViewCellで 7つのoffscreenを取り除けました すばらしい改善ですが もう1つ問題があります ナビゲーターに戻りましょう
image viewを選択します 角が丸くなっています runtime issueのハイライトを見ると simple shape maskingと表示されます もう1度 runtime navigatorで詳しく見ましょう
“レイヤーがCAShapeLayerで マスクされ―” “四角か角丸四角 または楕円のパスが使われている” “代わりに cornerRadiusと maskToBoundsを設定し―” “適切な形のcontainer layerを使う” offscreenの原因は また別のレイヤーのマスキングでしたが 今回はsimple layerではありません コードを見て確認しましょう
こちらがimage viewです まずCAShapeLayerを作り― UIBezierPath APIでパスを作り それをマスクとして使っています shape layerが マスクとして適切な場合もありますが ここでは複雑な形でなく ただの角丸四角を生成していると指摘されています 我々がこのコードを書いた理由は 特殊な角丸四角を生成するためでした 通称“squircle”です
このタイプの四角はiOSでよく使われますが 方法が間違っていました iOS 13から cornerCurveプロパティを使って― squircleの形にすることができます shapeLayerを削除して― cornerRadiusとcornerCurveを continuousに設定しましょう
提供されているAPIを使って― また2つoffscreenを取り除けました これでoffscreenの数を最適化し 36からゼロにすることができました すばらしいことです
view debuggerに戻ると― レンダー関連以外で 他にもruntime issueが見られます 同僚のチャールズにこれを見せて― 最適化を頼みましょう これまでなら同じデバイスを使って― Appをつながなければなりませんでした でも今はview debuggerの状況をセーブし― 同僚にメールで送信して共有することができます フィードバック報告に添付もできます それにはFile 次にExport View Hierarchyを選択して
セーブをクリックし完了 これでビューやレイヤーや制約や runtime issuesが― 送信可能なファイルに リモートで協力しやすくなり― 不必要なoffscreenを取り除くのに 非常に役立ちます
以上 Xcode view debuggerで offscreenを修正する方法でした ではおさらいをしましょう
どのAppでも重要なのは― 必ず提供されたAPIを使うことです 影を作るならshadowPathを設定し― offscreen passを避けましょう 角の丸みには― cornerRadiusと cornerCurveプロパティを使いましょう マスクやcorner contentsを使って 角丸四角を作るのは避けましょう 不必要なoffscreenを招きます 多くの場合 UIBezirePathを作り cornerRadiusでレイヤーの境界を丸めれば うまくshadowPathが設定できます
次にマスキングを最適化しましょう maskToBoundsを使って 四角や丸角四角や楕円形にマスクします custom mask layersより有効です そしてマスクが必要か確認しましょう サブツリーのコンテンツが境界を越えないなら maskToBoundsを無効にしましょう
これらは推奨に過ぎません Instrumentsを使ってAppを解析し optimization opportunitiesを使いましょう offscreenを減らす上で― レイヤーツリーから シンプルかつ重要なヒントが得られます 最後に ビュー階層の状況がセーブ可能です チームと共有し― フィードバック報告に添付することもできます これらの推奨事項が render hitchを避ける上で役立つでしょう commit hitchについては― "Find and Fix Hitches in the Commit Phase"をご覧ください hitch time ratioを軽減させ スムーズにスクロールする上で― 皆さんの役に立つでしょう
ご視聴ありがとうございました
-
-
0:01 - Shadow Path
// Setup shadow properties view.layer.shadowColor = UIColor.black.cgColor view.layer.shadowOpacity = 0.5 // Set a shadow path on a basic layer view.layer.shadowPath = UIBezierPath(roundedRect: view.layer.bounds, cornerRadius: view.layer.cornerRadius).cgPath
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。