对于 GPU 计算是理想选择的工作负载,将 RenderScript 脚本迁移到 Vulkan compute 可以让您的应用更直接地控制 GPU 硬件,与使用其他 API 相比,可能会释放额外的性能。
以下是关于如何使用 Vulkan 计算着色器替换 RenderScript 脚本的高级概览。
Vulkan 初始化
与在 Kotlin 或 Java 中创建 RenderScript 上下文对象不同,执行以下步骤可以使用 NDK 创建 Vulkan 上下文。
创建 Vulkan 实例。
选择支持计算队列的 Vulkan 物理设备。
创建 Vulkan 逻辑设备,并获取计算队列。
(可选)您可以在 Android 上设置 Vulkan 验证层,以加快 Vulkan 应用的开发。
示例应用演示了如何在 VulkanContext.h
中初始化 Vulkan 上下文。要了解更多信息,请参阅 Vulkan 规范的初始化和设备和队列部分。
Vulkan 分配
RenderScript 分配可以迁移到Vulkan 存储图像或Vulkan 存储缓冲区。为了更好地读取只读图像的性能,可以使用带有 fetch 操作的采样图像,可以是组合图像采样器,也可以是具有不同的采样器和采样图像绑定。
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 着色语言 (GLSL) 编写,然后编译成标准可移植中间表示-V (SPIR-V) 格式。
有关将着色器集成到应用中的详细信息和说明,请参阅Android 上的 Vulkan 着色器编译器。
脚本全局变量的适配
根据脚本全局变量的特性,对于着色器内部不修改的全局变量,我们建议使用专用化常量、推送常量或统一缓冲区对象。
- 专用化常量:建议用于在内核调用之间大部分保持一致的脚本全局变量。更改专用化常量的值需要重新创建计算流水线。
- 推送常量:建议用于大小小于
maxPushConstantsSize
(保证最小值:128 字节)的频繁更改的脚本全局变量。 - 统一缓冲区:建议用于大小大于推送常量限制的频繁更改的脚本全局变量。
对于在着色器内部更改的全局变量,您可以使用Vulkan 存储图像或Vulkan 存储缓冲区。
计算
您需要创建 Vulkan 计算流水线才能让 GPU 执行计算着色器。
创建 Vulkan 计算流水线
示例应用中的 ComputePipeline.h
文件演示了如何创建 Vulkan 计算流水线。
要在 Vulkan 中使用已编译的 SPIR-V 着色器,请按以下方式构建 Vulkan 计算流水线
- 使用已编译的 SPIR-V 着色器创建着色器模块。
- 创建指定资源绑定的描述符集布局(更多详细信息请参见分配)。
- 从描述符集布局创建描述符集。
- 从描述符集布局创建流水线布局。
- 使用着色器模块和流水线布局创建计算流水线。
要了解更多信息,请参阅 Vulkan 规范的计算流水线部分。
开始计算
要使用计算流水线开始计算
- 使用 Vulkan 资源更新描述符集。
- 创建 Vulkan 命令缓冲区,并记录以下命令
- 绑定流水线和描述符集。
- 调度计算工作组。
- 将命令缓冲区提交到计算队列。
- 等待队列,或者可选地返回同步栅栏。
要将多个内核串联起来(例如,迁移使用 ScriptGroup
的代码),请将它们记录在单个命令缓冲区中,并使用内存屏障进行同步。
示例应用演示了两个计算任务
- HUE 旋转:一个简单的计算任务,只有一个计算着色器。请参见
ImageProcessor::rotateHue
获取代码示例。 - 模糊:一个更复杂的计算任务,顺序执行两个计算着色器。请参见
ImageProcessor::blur
获取代码示例。
要了解有关命令缓冲区或内存屏障的更多信息,请参阅 Vulkan 规范中称为命令缓冲区和内存屏障的部分。