スクリプトを Vulkan に移行する

GPU コンピューティングが理想的なワークロードの場合、RenderScript スクリプトを Vulkan コンピューティングに移行すると、アプリが GPU ハードウェアをより直接的に制御できるようになり、他の API よりもパフォーマンスが向上する可能性があります。

以下に、Vulkan コンピューティング シェーダーを使用して RenderScript スクリプトを置き換える方法の概要を説明します。

Vulkan の初期化

Kotlin または Java で RenderScript コンテキスト オブジェクトを作成する代わりに、次の手順に沿って NDK を使用して Vulkan コンテキストを作成します。

  1. Vulkan インスタンスを作成します。

  2. コンピューティング キューに対応している Vulkan 物理デバイスを選択します。

  3. Vulkan 論理デバイスを作成し、コンピューティング キューを取得します。

必要に応じて、Android で Vulkan 検証レイヤを設定して、Vulkan アプリの開発を高速化できます。

このサンプルアプリでは、VulkanContext.h で Vulkan コンテキストを初期化する方法を紹介します。 詳しくは、Vulkan 仕様の初期化デバイスとキューのセクションをご覧ください。

Vulkan の割り当て

RenderScript の割り当ては、Vulkan ストレージ イメージまたは Vulkan ストレージ バッファに移行できます。読み取り専用画像のパフォーマンスを向上させるには、結合画像サンプラーとして、または個別のサンプラーサンプリング画像のバインディングとして、取得オペレーションでサンプリング画像を使用します。

Vulkan リソースは Vulkan 内で割り当てられます。他の Android コンポーネントとやり取りする際にメモリコピーのオーバーヘッドを回避するには、VK_ANDROID_external_memory_android_hardware_buffer 拡張機能を使用して Android AHardwareBuffer を Vulkan にインポートすることを検討してください。この拡張機能は、Vulkan 1.1 をサポートするすべての Android デバイスで利用できます。詳しくは、FEATURE_VULKAN_HARDWARE_VERSION をご覧ください。

このサンプルアプリでは、VulkanResources.h で Vulkan リソースを作成する方法を紹介します。 詳しくは、Vulkan 仕様のリソースの作成セクションとリソース記述子のセクションをご覧ください。

Vulkan コンピューティング シェーダーへの変換

RenderScript スクリプトを Vulkan コンピューティング シェーダーに変換する必要があります。また、RenderScript グローバルの使用方法に応じてコードを調整する必要があります。

Vulkan コンピューティング シェーダーの作成

Vulkan コンピューティング シェーダーは通常、OpenGL Shading Language(GLSL)で記述され、Standard Portable Intermediate Representation-V(SPIR-V)形式にコンパイルされます。

シェーダーをアプリに追加する方法の詳細と手順については、Android の Vulkan シェーダー コンパイラをご覧ください。

スクリプト グローバルの調整

スクリプト グローバルの特性に基づいて、シェーダー内で変更されていないグローバルには、特殊定数、プッシュ定数、または均一なバッファ オブジェクトを使用することをおすすめします。

  • 特殊定数: カーネルの呼び出し全体で一貫性のあるスクリプト グローバルに推奨されます。特殊定数の値を変更するには、コンピューティング パイプラインを再作成する必要があります。
  • プッシュ定数: 頻繁に変更され、サイズが maxPushConstantsSize より小さいスクリプト グローバルに推奨されます(保証されている最小数: 128 バイト)。
  • ユニフォーム バッファ: プッシュ定数の上限よりも大きいサイズのスクリプト グローバルに推奨されます。

シェーダー内で変更されるグローバルには、Vulkan ストレージ イメージまたは Vulkan ストレージ バッファを使用できます。

計算

GPU がコンピューティング シェーダーを実行できるようにするには、Vulkan コンピューティング パイプラインを作成する必要があります。

Vulkan コンピューティング パイプラインの作成

サンプルアプリの ComputePipeline.h ファイルは、Vulkan コンピューティング パイプラインを作成する方法を示しています。

コンパイルされた SPIR-V シェーダーを Vulkan で使用するには、次のように Vulkan コンピューティング パイプラインを構築します。

  1. コンパイル済みの SPIR-V シェーダーでシェーダー モジュールを作成します。
  2. リソース バインドを指定して記述子セットのレイアウトを作成します(詳しくは、割り当てをご覧ください)。
  3. 記述子セット レイアウトから記述子セットを作成します。
  4. 記述子セット レイアウトからパイプライン レイアウトを作成します。
  5. シェーダー モジュールとパイプライン レイアウトを使用してコンピューティング パイプラインを作成します。

詳細については、Vulkan 仕様の Compute Pipelines をご覧ください。

計算の開始

コンピューティング パイプラインを使用して計算を開始する手順は、次のとおりです。

  1. Vulkan リソースを使用して記述子セットを更新します。
  2. Vulkan コマンド バッファを作成し、次のコマンドを記録します。
    1. パイプラインと記述子セットをバインドします。
    2. コンピューティング ワークグループをディスパッチします。
  3. コマンド バッファをコンピューティング キューに送信します。
  4. キューで待機するか、必要に応じて同期フェンスを返します。

複数のカーネルをまとめて接続する(たとえば、ScriptGroup を使用してコードを移行する)には、これらを 1 つのコマンド バッファに記録し、メモリバリアと同期させます。

サンプルアプリでは、次の 2 つのコンピューティング タスクを行います。

  • HUE ローテーション: 単一のコンピューティング シェーダーを使用したシンプルなコンピューティング タスク。コードサンプルについては、ImageProcessor::rotateHue をご覧ください。
  • ぼかし: 2 つのコンピューティング シェーダーを順次実行する複雑なコンピューティング タスク。コードサンプルについては、ImageProcessor::blur をご覧ください。

コマンド バッファやメモリバリアについて詳しくは、Vulkan 仕様のコマンド バッファメモリバリアというセクションをご覧ください。