对于 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 Allocation 可以迁移到 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 着色语言 (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
迁移代码),请将它们记录在单个命令缓冲区中并使用内存屏障进行同步。
该示例应用演示了两个计算任务
- 色调旋转:一个简单的计算任务,具有单个计算着色器。有关代码示例,请参阅
ImageProcessor::rotateHue
。 - 模糊:一个更复杂的计算任务,依次执行两个计算着色器。有关代码示例,请参阅
ImageProcessor::blur
。
要了解有关命令缓冲区或内存屏障的更多信息,请参阅 Vulkan 规范中名为命令缓冲区和内存屏障的部分。