鸿蒙开发-粒子太多渲染太慢?HPS高性能着色器帮你排序
游戏里需要 GPU 排序?试试 XEngine 高性能着色器(HPS)
你有没有遇到过这种场景:你的游戏里有大量的粒子、精灵或者透明物体,需要按照深度排序才能正确渲染。在 CPU 上排序当然可以,但数据要来回搬运,性能不好。如果能在 GPU 上直接排序,省去了数据搬运的开销,那该多好。
XEngine 的 HPS(High Performance Shader,高性能着色器)特性就是做这个的。它提供了一个 GPU 端的基数排序(Radix Sort)能力,让你可以在 Vulkan 命令缓冲里直接录制排序命令,由 GPU 高效完成排序。
什么是基数排序
下面是 HPS 基数排序的整体使用流程:
基数排序是一种非比较排序算法。简单说,它不是通过比较元素大小来排序,而是按照每个元素的"位"(从低位到高位)逐轮分配和收集。基数排序的时间复杂度是 O(nk),其中 n 是元素数量,k 是位数。对于 32 位整数来说,k=32,所以效率很高。
GPU 上的基数排序特别适合大规模数据的排序,因为 GPU 有大量的并行计算单元,可以同时处理很多数据。
查询支持
HPS 基数排序需要通过扩展查询接口确认设备支持:
uint32_t propertyCount = 0;
HMS_XEG_EnumerateDeviceExtensionProperties(physicalDevice, &propertyCount, NULL);
XEG_ExtensionProperties* properties = malloc(sizeof(XEG_ExtensionProperties) * propertyCount);
HMS_XEG_EnumerateDeviceExtensionProperties(physicalDevice, &propertyCount, properties);
bool supported = false;
for (uint32_t i = 0; i < propertyCount; i++) {
if (strcmp(properties[i].extensionName, XEG_HPS_RADIX_SORT_EXTENSION_NAME) == 0) {
supported = true;
break;
}
}
free(properties);
if (!supported) return;
XEG_HPS_RADIX_SORT_EXTENSION_NAME 的值是 "XEG_hps_radix_sort"。这个特性起始版本是 6.0.0(20)。
创建 XEG_HPS 对象
确认支持之后,创建 HPS 对象:
XEG_HPS hps = VK_NULL_HANDLE;
XEG_HPSCreateInfo createInfo = {
// 创建参数
};
VkResult result = HMS_XEG_CreateHPS(device, &createInfo, &hps);
if (result != VK_SUCCESS) {
// 创建失败
return;
}
HMS_XEG_CreateHPS 的三个参数:
device:VkDevicepCreateInfo:XEG_HPSCreateInfo结构体指针,不允许为空pHps:输出句柄的指针
返回 VK_SUCCESS 表示创建成功。
录制排序命令
有了 HPS 对象之后,你可以在命令缓冲里录制基数排序命令:
XEG_HPSRadixSortDescription description = {
// 排序的输入输出信息
};
VkResult result = HMS_XEG_CmdRadixSortHPS(commandBuffer, hps, &description);
if (result != VK_SUCCESS) {
// 命令录制失败
}
HMS_XEG_CmdRadixSortHPS 的三个参数:
commandBuffer:VkCommandBufferhps:已创建的XEG_HPS对象pDescription:XEG_HPSRadixSortDescription结构体指针,不允许为空
返回 VK_SUCCESS 表示命令录制成功。
XEG_HPSRadixSortDescription 描述了排序的具体信息:要排序的数据在哪里、排序结果写到哪里、排序的 key 是什么等等。这些信息的具体字段需要参考结构体的定义。
销毁对象
用完之后记得销毁:
HMS_XEG_DestroyHPS(hps);
完整流程
// 1. 查询扩展
uint32_t propertyCount = 0;
HMS_XEG_EnumerateDeviceExtensionProperties(physicalDevice, &propertyCount, NULL);
XEG_ExtensionProperties* properties = malloc(sizeof(XEG_ExtensionProperties) * propertyCount);
HMS_XEG_EnumerateDeviceExtensionProperties(physicalDevice, &propertyCount, properties);
bool supported = false;
for (uint32_t i = 0; i < propertyCount; i++) {
if (strcmp(properties[i].extensionName, XEG_HPS_RADIX_SORT_EXTENSION_NAME) == 0) {
supported = true;
break;
}
}
free(properties);
if (!supported) return;
// 2. 创建 HPS 对象
XEG_HPS hps = VK_NULL_HANDLE;
XEG_HPSCreateInfo createInfo = { /* ... */ };
HMS_XEG_CreateHPS(device, &createInfo, &hps);
// 3. 每帧:录制排序命令
XEG_HPSRadixSortDescription desc = { /* 当前帧的排序参数 */ };
HMS_XEG_CmdRadixSortHPS(commandBuffer, hps, &desc);
// 4. 提交命令缓冲
vkQueueSubmit(queue, 1, &submitInfo, fence);
// 5. 排序结果在 GPU 内存里,可以直接用于后续的渲染步骤
// 6. 退出时销毁
HMS_XEG_DestroyHPS(hps);
和 XEG_StructureType 的关系
HPS 使用了 XEG_StructureType 枚举里的三个值:
XEG_STRUCTURE_TYPE_HPS_CREATE_INFO = 1001XEG_STRUCTURE_TYPE_HPS_RADIX_SORT = 1002XEG_STRUCTURE_TYPE_HPS_RADIX_SORT_DESCRIPTION = 1003
如果你的结构体需要设置 sType 字段,记得用对应的枚举值。
实际应用场景
GPU 排序和 CPU 排序的对比:
-
粒子系统排序:大量透明粒子需要按照深度排序才能正确渲染。在 GPU 上排序避免了数据回读到 CPU 的开销。
-
精灵渲染:2D 游戏里大量的精灵需要按层级排序。GPU 排序可以让这个过程更快。
-
光线追踪加速结构:有些光线追踪算法需要对图元进行空间排序来构建加速结构。
-
物理模拟:碰撞检测中经常需要对物体按空间位置排序。
使用建议
-
数据要提前上传到 GPU:基数排序是在 GPU 上执行的,所以待排序的数据必须在 GPU 内存里。如果你的数据在 CPU 上,需要先上传。
-
排序结果也在 GPU 上:排序完成后,结果直接存在 GPU 内存里,可以无缝用于后续的渲染步骤。如果你需要在 CPU 上使用排序结果,需要回读。
-
命令录制而非立即执行:
HMS_XEG_CmdRadixSortHPS只是把排序命令录制到命令缓冲里,不会立即执行。你需要提交命令缓冲之后排序才会真正运行。 -
检查返回值:创建和录制命令都有返回值,记得检查是否为
VK_SUCCESS。
HPS 是一个很实用的 GPU 计算工具。虽然它目前只提供了基数排序能力,但这个能力在游戏开发中用得非常多。如果你的游戏有大量的排序需求,HPS 可以帮你省下不少 CPU 时间。
更多推荐


所有评论(0)