こんにちは
今週末、2011年のWWDCビデオを見始めました。 iOSに関する非常に興味深いトピックを見つけました。私のお気に入りはパフォーマンスとグラフィックスでしたが、そのうち2つは矛盾しているようです。もちろん、私が理解できなかったものがあります。私が話しているセッションは、UIKitレンダリングの理解-121とアプリの磨き-105です。
残念ながら、2011年のサンプルコードはまだダウンロードできません。そのため、全体像を把握するのはかなり困難です。あるセッションで、スクロールビューなどでの視覚化中にはほとんどの場合オフスクリーンレンダリングを回避する必要があると説明しています。-drawRectメソッド内のほとんどすべてを描画するサンプルコードのパフォーマンスの問題を修正しています。他のセッションでは、(テーブルビューの)パフォーマンスの問題は、テーブルのセルの-drawRectメソッドのコードが多すぎることが原因のようです。
システムでオフスクリーンレンダリングが必要な場合、最初ははっきりしません。ビデオで、cornerRadious、shadowOffset、shadowColorなどのいくつかのクオーツ関数がそれを必要とすることを確認しましたが、一般的なルールは存在しますか?
2つ目は、よく理解したかどうかはわかりませんが、オフスクリーンレンダリングがない場合は、レイヤーまたはビューを追加するのがよいようです。誰かがそれについて明かしてくれることを願っています。
ありがとう、
アンドレア
どこにも書き留められているルールはないと思いますが、うまくいけば、これが役立つでしょう:
まず、いくつかの定義を明らかにしましょう。オフスクリーンレンダリングはオンスクリーンと同じくらい高速である可能性があるため、オフスクリーンレンダリングとオンスクリーンレンダリングはほとんどの場合、最も重要な問題ではないと思います。主な問題は、レンダリングがハードウェアで行われるかソフトウェアで行われるかです。
また、レイヤーとビューの使用には実用的な違いはほとんどありません。ビューはCALayerの薄いラッパーにすぎず、ほとんどの場合、パフォーマンスを大幅に低下させることはありません。 CAShapeLayerやCATileLayerなどでビューをサポートする場合は、+ layerClassメソッドを使用して、ビューで使用されるレイヤーのタイプをオーバーライドできます。
一般に、iOSでは、ピクセルエフェクトとQuartz/Core Graphics描画はハードウェアアクセラレーションではなく、他のほとんどのものです。
以下はハードウェアアクセラレーションではないため、ソフトウェアで実行する必要があります(オフスクリーン)。
DrawRectで行われたすべてのこと。ビューにdrawRectがある場合、それが空の場合でも、描画はハードウェアで行われず、パフォーマンスが低下します。
ShouldRasterizeプロパティがYESに設定されているレイヤー。
マスクまたはドロップシャドウのあるレイヤー。
テキスト(UILabels、CATextLayers、Core Textなどを含むあらゆる種類)。
CGContextを使用して自分で行う描画(画面上または画面外)。
他のほとんどのものはハードウェアで高速化されているため、はるかに高速です。しかし、これはあなたがそれが何をしていると思うかを意味しないかもしれません。
上記のタイプの描画はいずれも、ハードウェアアクセラレーションによる描画に比べて低速ですが、フレームごとに発生する必要がないため、必ずしもアプリの速度が低下するとは限りません。たとえば、ビューへのドロップシャドウの描画は最初は遅くなりますが、描画後はキャッシュされ、ビューのサイズまたは形状が変更された場合にのみ再描画されます。
同じことがラスター化されたビューやカスタムのdrawRectを含むビューにも当てはまります。ビューは通常、フレームごとに再描画されるのではなく、一度描画されてからキャッシュされるため、境界が変更されない限り、最初にビューを設定した後のパフォーマンスは低下しません。その上でsetNeedsDisplayを呼び出します。
良いパフォーマンスを得るための秘訣は、フレームごとに変化するビューにソフトウェア描画を使用しないことです。たとえば、アニメーションのベクターシェイプが必要な場合、drawRectやCore GraphicsよりもCAShapeLayerまたはOpenGLを使用するとパフォーマンスが向上します。ただし、図形を一度描画した後、変更する必要がない場合は、それほど大きな違いはありません。
同様に、フレームレートが低下するため、アニメーションビューにドロップシャドウを配置しないでください。ただし、フレームごとに変化しないビューの影は、あまり悪影響を与えません。
もう1つの注意点は、ビューのセットアップ時間を遅くすることです。たとえば、すべてのテキストに影付きのテキストのページがあるとします。テキストと影のすべてをソフトウェアでレンダリングする必要があるため、最初に描画するのに非常に長い時間がかかりますが、一度描画すると高速になります。したがって、アプリケーションのロード時にこのビューを事前に設定し、そのビューのコピーをメモリに保持して、ユーザーがビューが最初に画面に表示されたときにそのビューが表示されるのを待つ必要がないようにします。
これが、WWDCビデオで明らかに矛盾している理由です。フレームごとに変化しない大きな複雑なビューの場合、ソフトウェアで一度描画すると(その後キャッシュされ、再描画する必要はありません)、ハードウェアでフレームごとに再合成するよりもパフォーマンスが向上します。初回の描画は遅くなります。
ただし、テーブルセルのように常に再描画する必要があるビューの場合(セルはリサイクルされるため、1つのセルが画面外にスクロールするたびに再描画する必要があり、別の行として別の側にスクロールすると再利用されます)、ソフトウェア描画物事を遅くします。
オフスクリーンレンダリングは、今日のiOSレンダリングで最悪のトピックの1つです。 AppleのUIKitエンジニアがオフスクリーンレンダリングに言及するとき、それは非常に特定の意味を持ち、多くのサードパーティのiOS開発ブログが間違っています。
「drawRect:」をオーバーライドすると、CPUを介して描画し、ビットマップを吐き出します。ビットマップはパッケージ化され、レンダーサーバーであるiOSに存在する別のプロセスに送信されます。理想的には、レンダリングサーバーはデータを画面に表示するだけです。
ドロップシャドウをオンにするなど、CALayerのプロパティをいじる場合、GPUは追加の描画を実行します。この追加作業は、UIKitエンジニアが「オフスクリーンレンダリング」と言ったときに意味します。これは常にハードウェアで実行されます。
オフスクリーン描画の問題は、必ずしも描画ではありません。 GPUが描画先を切り替えるため、オフスクリーンパスではコンテキストスイッチが必要です。この切り替え中、GPUはアイドル状態です。
オフスクリーンパスをトリガーするプロパティの完全なリストはわかりませんが、Core Animation Instrumentの「カラーオフスクリーンレンダリングレイヤー」トグルでこれを診断できます。 alpha以外のプロパティはすべてオフスクリーンパスを介して実行されると思います。
初期のiOSハードウェアでは、「drawRectですべてを行う」と言うのが合理的でした。最近のGPUの方が優れており、UIKitにはshouldRasterizeなどの機能があります。今日、それは、drawRectで費やされた時間、オフスクリーンパスの数、およびブレンドの量の間のバランスをとる行為です。詳細については、2014年のWWDCセッション419「iOSアプリ用の高度なグラフィックスとアニメーション」をご覧ください。
とは言っても、舞台裏で何が起こっているのかを理解し、頭の後ろに置いておけば、何も気が狂うことはありませんが、最も簡単な解決策から始めるべきです。次に、サポートしている最も遅いハードウェアでテストします。 60FPSに達していない場合は、Instrumentsを使用して測定し、把握してください。ボトルネックの可能性はいくつかあります。データを使用して物事を診断していない場合は、推測しているだけです。
グラフィックスパフォーマンスの最大のボトルネックは、オフスクリーンレンダリングとブレンドです。これらは、アニメーションのすべてのフレームで発生する可能性があり、不安定なスクロールを引き起こす可能性があります。
オフスクリーンレンダリング(ソフトウェアレンダリング)は、GPUに渡す前にソフトウェア(オフスクリーン)で描画する必要がある場合に発生します。ハードウェアは、マスクとシャドウを使用したテキストのレンダリングと高度な構成を処理しません。
以下はオフスクリーンレンダリングをトリガーします。
マスク付きのレイヤー(layer.mask
)
layer.masksToBounds
/view.clipsToBounds
がtrueであるレイヤー
layer.allowsGroupOpacity
がYESに設定され、layer.opacity
が1.0未満のレイヤー
ビュー(またはレイヤー)はいつオフスクリーンレンダリングを必要としますか?
ドロップシャドウのあるレイヤー(layer.shadow*
)。
修正方法に関するヒント: https://markpospesel.wordpress.com/tag/performance/
layer.shouldRasterize
がtrueのレイヤー
layer.cornerRadius
、layer.edgeAntialiasingMask
、layer.allowsEdgeAntialiasing
を含むすべてのレイヤー
layer.borderWith
およびlayer.borderColor
を含むレイヤー
参照/証拠がない
テキスト(UILabel
、CATextLayer
、Core Text
などを含む任意の種類)。
drawRect:
のCGContext
で行うほとんどの描画。空の実装でもオフスクリーンでレンダリングされます。
この投稿では、ブレンドやパフォーマンスに影響を与えるその他の事項について説明します: iOSでオフスクリーンレンダリング、ブレンド、layoutSubviewsをトリガーするものは何ですか?