GPU コンピューティングが理想的なワークロードの場合、RenderScript スクリプトを Vulkan コンピューティングに移行すると、アプリが GPU ハードウェアをより直接的に制御できるようになり、他の API よりもパフォーマンスが向上する可能性があります。
以下に、Vulkan コンピューティング シェーダーを使用して RenderScript スクリプトを置き換える方法の概要を説明します。
Vulkan の初期化
Kotlin または Java で RenderScript コンテキスト オブジェクトを作成する代わりに、次の手順に沿って NDK を使用して Vulkan コンテキストを作成します。
Vulkan インスタンスを作成します。
コンピューティング キューに対応している Vulkan 物理デバイスを選択します。
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 コンピューティング パイプラインを構築します。
- コンパイル済みの SPIR-V シェーダーでシェーダー モジュールを作成します。
- リソース バインドを指定して記述子セットのレイアウトを作成します(詳しくは、割り当てをご覧ください)。
- 記述子セット レイアウトから記述子セットを作成します。
- 記述子セット レイアウトからパイプライン レイアウトを作成します。
- シェーダー モジュールとパイプライン レイアウトを使用してコンピューティング パイプラインを作成します。
詳細については、Vulkan 仕様の Compute Pipelines をご覧ください。
計算の開始
コンピューティング パイプラインを使用して計算を開始する手順は、次のとおりです。
- Vulkan リソースを使用して記述子セットを更新します。
- Vulkan コマンド バッファを作成し、次のコマンドを記録します。
- パイプラインと記述子セットをバインドします。
- コンピューティング ワークグループをディスパッチします。
- コマンド バッファをコンピューティング キューに送信します。
- キューで待機するか、必要に応じて同期フェンスを返します。
複数のカーネルをまとめて接続する(たとえば、ScriptGroup
を使用してコードを移行する)には、これらを 1 つのコマンド バッファに記録し、メモリバリアと同期させます。
サンプルアプリでは、次の 2 つのコンピューティング タスクを行います。
- HUE ローテーション: 単一のコンピューティング シェーダーを使用したシンプルなコンピューティング タスク。コードサンプルについては、
ImageProcessor::rotateHue
をご覧ください。 - ぼかし: 2 つのコンピューティング シェーダーを順次実行する複雑なコンピューティング タスク。コードサンプルについては、
ImageProcessor::blur
をご覧ください。
コマンド バッファやメモリバリアについて詳しくは、Vulkan 仕様のコマンド バッファとメモリバリアというセクションをご覧ください。