見出し画像

Vision Pro の アプリ構築 (4) - レイヤーベースのコンテンツの描画

以下の記事が面白かったので、簡単にまとめました。

Drawing sharp layer-based content in visionOS


前回

1. はじめに

アプリで「Core Animation Layer」を直接使用する場合は、レイヤーコードを更新して、必要に応じてコンテンツの高解像度バージョンを描画します。「SwiftUI」と「UIKit」ビューは、「Core Animation Layer」を使用して、インターフェイスコンテンツを効率的に管理します。ビューがコンテンツを描画すると、基礎となるレイヤーはそのコンテンツをキャプチャし、その後のレンダリング操作を改善するためにキャッシュします。

ほとんどのAppleプラットフォームの「Core Animation」は、画面と同じ解像度でレイヤーをラスタライズしますが、visionOSの「Core Animation」は、コンテンツの明瞭さとパフォーマンスの両方を最大化するために、異なる解像度でラスタライズできます。システムは、その人の目に従い、可能な限り最高の解像度で目の前のコンテンツをレンダリングします。この焦点領域以外では、システムはGPUワークロードを削減するために、徐々に低い解像度でコンテンツをレンダリングします。コンテンツは人の周辺視野にあるため、これらの低解像度はコンテンツの明瞭さに影響を与えません。人の目が動き回るにつれて、システムは焦点の変化に合わせて異なる解像度でコンテンツを再描画します。

カスタムCALayerオブジェクトを使用してコンテンツを配信する場合は、さまざまな解像度での描画をサポートするようにカスタムレイヤーを設定できます。この追加の設定ステップを実行しない場合、各レイヤーは@2xスケールファクターでコンテンツをラスタライズします。これは、ほとんどのコンテンツに十分であり、レイヤーがRetinaディスプレイで提供するものと一致します。ただし、異なる解像度で描画することを選択した場合、レイヤーはvisionOSで最大@8xスケールファクターでコンテンツをラスタライズし、テキストとベクターベースのコンテンツに重要な詳細を追加します。

2. カスタムレイヤーの動的スケーリング

動的コンテンツスケーリングは、すべての「Core Animation Layer」でデフォルトでオフになっており、フレームワークまたはアプリはこのサポートを明示的にオンにする必要があります。インターフェイスが「SwiftUI」または「UIKit」ビューのみを使用している場合は、この機能をサポートするために何もする必要はありません。「SwiftUI」と「UIKit」は、SFシンボルやその他のベクターベースのアートワークを使用したテキストビューや画像ビューなど、追加された詳細の恩恵を受けるビューを自動的に有効にします。ただし、フレームワークでは、UIViewViewを含むすべてのビューで機能を有効にするわけではありません。

visionOSインターフェイスにカスタム「Core Animation Layer」が含まれている場合は、ベクターベースのコンテンツを含む CALayer の wants を有効にできます。このプロパティをtrueに設定すると、レイヤーのコンテンツを異なる解像度でレンダリングすることをサポートすることをシステムに通知します。ただし、この設定は、システムがコンテンツに動的コンテンツスケーリングを適用することを保証するものではありません。レイヤーが互換性のない機能やテクニックを使用して描画する場合、システムは機能を無効にすることができます。

次の例は、CAText でこの機能を有効にする方法を示しています。レイヤーを設定した後、wants をtrueに設定し、レイヤーをレイヤー階層に追加します。

let layer = CATextLayer()


layer.string = "Hello, World!"
layer.foregroundColor = UIColor.black.cgColor
layer.frame = parentLayer.bounds


// Setting this property to true enables content scaling 
// and calls setNeedsDisplay to redraw the layer's content.
layer.wantsDynamicContentScaling = true


parentLayer.addSublayer(layer)

動的コンテンツスケーリングは、レイヤーにテキストまたはベクターベースのコンテンツが含まれている場合に最も効果的です。レイヤーで次のいずれかを行う場合は、機能を有効にしないでください。

contents を使用してレイヤーのコンテンツを設定。
・主にビットマップベースのコンテンツを描画。
・レイヤーの内容を短期間で繰り返し再描画。

CAShape は、wants の値を無視し、常に動的コンテンツスケーリングを有効にします。他の「Core Animation Layer」では、その機能を利用するには明示的に有効にする必要があります。

3. レイヤーのコンテンツを動的に描画

動的コンテンツスケーリングでは、所定の方法のいずれかを使用してレイヤーのコンテンツを描画する必要があります。CALayer のカスタムサブクラスを定義する場合は、draw(in:) でレイヤーのコンテンツを描画します。CALayer を使用してレイヤーのコンテンツを描画する場合は、代わりにデリゲートの draw(_:in:) を使用してください。

レイヤーの動的コンテンツスケーリングを有効にすると、システムは後で再生するためにアプリの描画コマンドをキャプチャします。人の目が動くと、システムは誰かが直接見るときにより高い解像度でレイヤーを描画し、それ以外の場合はより低い解像度でレイヤーを描画します。再描画操作は、その人が見ているものを暗黙的に伝えるため、システムはアプリのプロセスの外で実行します。システムにこれらの操作を処理させることは、アプリに高解像度描画の利点を与えながら、その人のプライバシーを維持します。

一部の「Core Graphics」ルーチンは、動的コンテンツスケーリングと互換性がありません。レイヤーの動的コンテンツスケーリングを有効にしても、レイヤーが次のいずれかを使用する場合、システムは自動的に機能を無効にします。

・Core Graphics Shader。
・意図、品質、その他のビットマップ関連のプロパティを設定するAPI。たとえば、CGContextSetを呼ばないでください。
・コンテンツを描画するためのCGBitmapContext

アプリがタイマーベースのアニメーションを作成する場合は、描画方法を使用してレイヤーの変更をアニメーション化しないでください。短時間でレイヤーでsetDisplay() を繰り返し呼び出すと、システムはレイヤーを複数回連続して描画します。visionOSは高解像度でレイヤーを描画するのに少し余分な時間を必要とするため、再描画要求ごとに作業を強制的に捨てることを余儀なくされます。より良い選択肢は、同じ効果を達成するためにレイヤーベースのプロパティをアニメーション化するか、必要に応じて CAShape を使用してパスをアニメーション化することです。

4. レイヤー階層を変更してパフォーマンスを向上

レイヤーのバッキングストアは、低解像度よりも高解像度でより多くのメモリを消費します。動的コンテンツスケーリングを有効にする前と後のアプリのメモリ使用量を測定し、増加したメモリコストが利益に見合う価値があることを確認します。アプリのメモリ使用量が増加しすぎる場合は、動的コンテンツスケーリングを採用するレイヤーを制限してください。また、次の方法で各レイヤーが使用するメモリの量を減らすこともできます。

・レイヤーを可能な限り最小サイズにしてください。より大きなレイヤーは、特により高い解像度で、かなり多くのメモリを必要とします。パディングや余分なスペースを排除して、レイヤーのサイズをコンテンツのサイズに一致させます。

・複雑なコンテンツを異なるレイヤーに分離します。すべてを1つのレイヤーに描画する代わりに、複数のレイヤーからコンテンツを構築し、同じ結果を達成するために階層的に配置します。実際に必要なレイヤーでのみ、動的コンテンツスケーリングを有効にします。

・可能な限り、レイヤープロパティを使用して特殊効果を適用します。描画中に効果を適用するには、レイヤーのサイズを大きくする必要があるかもしれません。たとえば、描画中ではなく、レイヤーの transform にスケールと回転の効果を適用します。

・レイヤーのコンテンツを事前に異なる解像度で描画したり、画像をキャッシュしたりしないでください。複数の画像を維持するには、より多くのメモリが必要です。画像をキャッシュする場合は、@2xスケールファクターでのみ描画します。

・描画コードを使って1つの画像を描画しないでください。レイヤーのコンテンツが画像で構成されている場合は、その画像をレイヤーの contents に直接割り当てます。

複雑な描画コードも、パフォーマンスの問題につながる可能性があります。多くのストロークを持つレイヤーは、より低いスケールの要因で迅速にレンダリングできますが、より大きなスケールでレンダリングするには計算的に複雑すぎる可能性があります。複雑なレイヤーがより高い解像度で正しくレンダリングされない場合は、動的コンテンツスケーリングをオフにして、レンダリング時間を再度測定してください。

次回



この記事が気に入ったらサポートをしてみませんか?