Vulkan デザイン ガイドライン

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

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

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

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

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

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

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

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

添付ファイルの読み込みと保存は、操作ごとにパフォーマンスのレベルが異なります。 たとえば、添付ファイルのコンテンツを保存する必要がない場合は、VK_ATTACHMENT_LOAD_OP_LOAD ではなく VK_ATTACHMENT_LOAD_OP_CLEARVK_ATTACHMENT_LOAD_OP_DONT_CARE を使用すれば、パフォーマンスを格段に向上できます。 同様に、添付ファイルの最終的な値をメモリに書き込んで後から使用する必要がない場合は、VK_ATTACHMENT_STORE_OP_STORE ではなく VK_ATTACHMENT_STORE_OP_DONT_CARE を使用すれば、パフォーマンスを大幅に向上できます。

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

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

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

アプリは、単純なアルゴリズムを使用して、使用方法に応じた最適なメモリの種類を選択することができます。 そのアルゴリズムは、2 つの条件を満たしている VkPhysicalDeviceMemoryProperties::memoryTypes 配列において、1 つ目のメモリの種類を選択します。その条件とは「メモリの種類をバッファまたはイメージに使用できること」と、「アプリで必要な最小限のプロパティがメモリの種類に存在していること」です。

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

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

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

各ドローコールで実行される変更など、最も頻度の高い変更には直接定数を使用します。