Vulkan 設計ガイドライン

Vulkan では、従来のグラフィック API と異なり、ドライバがアプリに対して特定の最適化(パイプラインの再利用など)を行いません。Vulkan を使用するアプリでは、このような最適化をアプリ自身で実装する必要があります。実装しないと、OpenGL ES を実行するアプリよりパフォーマンスが低下する場合があります。

特定のユースケースでは、アプリがより詳細な情報にアクセスできるため、ドライバより適切に最適化を実装できることがあります。そのため、Vulkan を使用するアプリを効果的に最適化すれば、アプリで OpenGL ES を使用する場合よりも高いパフォーマンスを実現することが可能になります。

このページでは、Vulkan で高パフォーマンスを実現するために Android アプリが実装できる最適化をいくつか紹介します。

ハードウェア アクセラレーション

ほとんどのデバイスではハードウェア アクセラレーションを使って実装された Vulkan 1.1 をサポートしていますが、小規模なサブセットではソフトウェア エミュレーションを使った実装をサポートしています。ソフトウェア ベースで Vulkan が実装されたデバイスを検出するため、アプリは vkGetPhysicalDeviceProperties を使用して返された構造体の deviceType フィールドをチェックします。 SwiftShader などの CPU ベースの実装は、VK_PHYSICAL_DEVICE_TYPE_CPU という値になっています。 特に SwiftShader であることを確認するため、アプリは同じ構造体の vendorIDdeviceID フィールドが SwiftShader に対してのみ使用される値になっているかどうかをチェックします。

パフォーマンスが重要なアプリでは、ソフトウェアでエミュレートされた Vulkan 実装を使用せず、OpenGL ES にフォールバックする必要があります。

レンダリング中に表示の回転を適用する

アプリの上向きの方向がデバイスの表示の方向と一致しない場合、コンポジターはアプリのスワップチェーン画像を回転して一致させます。この回転は画像を表示しながら実行されるため、画像を回転しない場合よりも電池の消費量が(ときには大幅に)増えます。

一方、画像を生成時に回転すると、追加の電池消費を要する場合でも少量で済みます。VkSurfaceCapabilitiesKHR::currentTransform フィールドは、コンポジターがウィンドウに適用する回転を示します。アプリは、レンダリング中にその回転を適用した後、VkSwapchainCreateInfoKHR::preTransform フィールドを使用して回転が完了したことを報告します。

1 フレームあたりのレンダリング パスを最小限にする

ほとんどのモバイル GPU アーキテクチャにおいて、レンダリング パスの開始と終了は負荷の高い操作になります。レンダリング操作をできる限り少ないレンダリング パスにまとめることで、アプリのパフォーマンスを改善できます。

アタッチメントの読み込みと保存は、操作ごとにパフォーマンスのレベルが異なります。たとえば、アタッチメントの内容を保存する必要がない場合、VK_ATTACHMENT_LOAD_OP_CLEAR または VK_ATTACHMENT_LOAD_OP_DONT_CARE を使用するほうが VK_ATTACHMENT_LOAD_OP_LOAD より格段に速くなります。同様に、アタッチメントの最終的な値をメモリに書き込んで後から使用する必要がない場合は、VK_ATTACHMENT_STORE_OP_DONT_CARE を使用するほうが VK_ATTACHMENT_STORE_OP_STORE よりパフォーマンスを大幅に改善できます。

また、ほとんどのレンダリング パスでは、アプリで深度やステンシルのアタッチメントを読み込んだり保存したりする必要がありません。このような場合は、アタッチメント画像の作成時に VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT フラグを使用すれば、アタッチメントに物理メモリを割り当てる必要がなくなります。このフラグには、OpenGL ES の glFramebufferDiscard と同様のメリットがあります。

適切なメモリの種類を選択する

デバイスのメモリを割り当てるとき、アプリはメモリの種類を選択する必要があります。メモリの種類によって、アプリがメモリを使用できる方法が変わります。またメモリの種類は、メモリのキャッシュ プロパティと一貫性プロパティを表します。使用可能なメモリの種類はデバイスによって異なります。また、メモリの種類ごとにパフォーマンス特性が異なります。

アプリは、単純なアルゴリズムを使って、各使用方法に応じた最適なメモリの種類を選択できます。このアルゴリズムは、VkPhysicalDeviceMemoryProperties::memoryTypes 配列内にあるメモリの種類のうち 2 つの条件を満たす最初のものを選択します。その条件とは、「バッファまたは画像に使用できること」と「アプリに必要な最小限のプロパティを備えていること」です。

モバイル システムは通常、CPU と GPU に別個の物理メモリヒープを使用することはありません。このようなシステムにおいて、独自の専用メモリに個別の GPU を使用するシステムと比べて、VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT は重要ではありません。このプロパティはアプリで必須にしないでください。

使用頻度に応じて記述子セットをグループ化する

使用頻度に応じて変わるリソース バインディングがある場合、すべてのリソースを各ドローに対して再バインドするのではなく、パイプラインごとに複数の記述子セットを使用します。たとえば、シーンごとのバインディング、マテリアルごとのバインディング、メッシュ インスタンスごとのバインディングに、それぞれ異なる記述子セットを使用できます。

各ドローの呼び出しで実行される変更など、最も頻度の高い変更には即値定数を使用します。