Vulkan 版自适应 VRS:用命令缓冲实现更灵活的着色率优化

如果你的图形引擎用的是 Vulkan 而不是 OpenGL ES,那自适应 VRS(Variable Rate Shading)的接入方式会有些不同。Vulkan 版的接口设计更符合 Vulkan 的风格——用句柄管理对象、用命令缓冲记录操作、用结构体传递参数。

这篇文章就来讲讲怎么在 Vulkan 环境下使用 XEngine 的自适应 VRS 特性。

Vulkan 版和 GLES 版有什么不同

最大的不同是 API 风格。GLES 版是直接调用函数,Vulkan 版是"创建对象 -> 录制命令 -> 销毁对象"的三步走模式。这和 Vulkan 本身的设计哲学是一致的:把操作记录到命令缓冲里,然后统一提交执行。

下面的流程图展示了 Vulkan 版自适应 VRS 的整体使用流程:

不支持

支持

查询扩展支持

是否支持自适应VRS?

退出或降级处理

创建 XEG_AdaptiveVRS 对象

每帧录制VRS命令到命令缓冲

提交命令缓冲执行

参数是否变化?

销毁旧对象并重建

退出时销毁对象

另外,Vulkan 版需要你通过 HMS_XEG_EnumerateDeviceExtensionProperties 接口来查询扩展支持,而不是 GLES 版的 HMS_XEG_GetString

第一步:查询扩展支持

在 Vulkan 里查询 XEngine 扩展:

// 先查询支持的扩展数量
uint32_t propertyCount = 0;
HMS_XEG_EnumerateDeviceExtensionProperties(physicalDevice, &propertyCount, NULL);

// 分配空间并查询扩展列表
XEG_ExtensionProperties* properties = (XEG_ExtensionProperties*)malloc(
    sizeof(XEG_ExtensionProperties) * propertyCount
);
HMS_XEG_EnumerateDeviceExtensionProperties(physicalDevice, &propertyCount, properties);

// 检查是否支持自适应 VRS
bool supportsAdaptiveVRS = false;
for (uint32_t i = 0; i < propertyCount; i++) {
    if (strcmp(properties[i].extensionName, XEG_ADAPTIVE_VRS_EXTENSION_NAME) == 0) {
        supportsAdaptiveVRS = true;
        break;
    }
}
free(properties);

if (!supportsAdaptiveVRS) {
    return;  // 不支持
}

HMS_XEG_EnumerateDeviceExtensionProperties 接受三个参数:

  • physicalDevice:当前使用的 Vulkan 物理设备
  • pPropertyCount:指向扩展数量的指针。当 pProperties 为 NULL 时,返回支持的扩展数量
  • pProperties:指向 XEG_ExtensionProperties 数组的指针,用于接收查询结果

查询成功返回 VK_SUCCESS,如果传入的数量不够则返回 VK_INCOMPLETE

XEG_ADAPTIVE_VRS_EXTENSION_NAME 的值是 "XEG_adaptive_vrs"

第二步:创建 XEG_AdaptiveVRS 对象

Vulkan 的哲学是"万物皆对象"。自适应 VRS 也不例外,你需要先创建一个 XEG_AdaptiveVRS 句柄:

XEG_AdaptiveVRS adaptiveVRS = VK_NULL_HANDLE;

XEG_AdaptiveVRSCreateInfo createInfo = {
    .sType = XEG_STRUCTURE_TYPE_RT_SHADOWAO_CREATE_INFO,  // 结构体类型
    // ... 其他创建参数
};

VkResult result = HMS_XEG_CreateAdaptiveVRS(device, &createInfo, &adaptiveVRS);
if (result != VK_SUCCESS) {
    // 创建失败,处理错误
    return;
}

HMS_XEG_CreateAdaptiveVRS 有三个参数:

  • device:当前使用的 VkDevice
  • pXegAdaptiveVRSCreateInfo:创建参数结构体的指针,不允许为空
  • pXegAdaptiveVRS:指向句柄的指针,创建成功后句柄会写入这里

当创建参数(比如分辨率、渲染区域等)发生变化时,你需要销毁旧对象、创建新对象。

第三步:录制自适应 VRS 命令

有了句柄之后,你可以在 Vulkan 命令缓冲里录制自适应 VRS 的计算命令:

// 准备描述信息
XEG_AdaptiveVRSDescription description = {
    // ... 每一帧需要更新的参数
};

// 录制命令到命令缓冲
HMS_XEG_CmdDispatchAdaptiveVRS(commandBuffer, adaptiveVRS, &description);

HMS_XEG_CmdDispatchAdaptiveVRS 有三个参数:

  • commandBuffer:当前记录命令的 VkCommandBuffer,这个命令缓冲必须被提交到 vkQueueSubmit 才会真正执行
  • xegAdaptiveVRS:已创建的 XEG_AdaptiveVRS 对象
  • pXegAdaptiveVRSDescription:参数结构体的指针,不允许为空。这个结构体里的信息每一帧都需要更新

注意,这个函数只是把命令"录制"到命令缓冲里,并不会立即执行。你需要在合适的时候提交命令缓冲,命令才会真正运行。

第四步:销毁对象

当你不再需要自适应 VRS 的时候(比如游戏退出或者分辨率切换),记得销毁对象:

HMS_XEG_DestroyAdaptiveVRS(adaptiveVRS);

这一步很重要。Vulkan 不像 OpenGL 那样有自动的资源回收机制,你创建的每一个对象都需要手动销毁。

完整流程

// 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_ADAPTIVE_VRS_EXTENSION_NAME) == 0) {
        supported = true;
        break;
    }
}
free(properties);
if (!supported) return;

// 2. 创建对象
XEG_AdaptiveVRS adaptiveVRS = VK_NULL_HANDLE;
XEG_AdaptiveVRSCreateInfo createInfo = { /* ... */ };
HMS_XEG_CreateAdaptiveVRS(device, &createInfo, &adaptiveVRS);

// 3. 每帧:录制命令
XEG_AdaptiveVRSDescription desc = { /* 当前帧参数 */ };
HMS_XEG_CmdDispatchAdaptiveVRS(commandBuffer, adaptiveVRS, &desc);

// 4. 退出时销毁
HMS_XEG_DestroyAdaptiveVRS(adaptiveVRS);

和 GLES 版的对比

下面的流程图对比了 GLES 版和 Vulkan 版在执行方式上的差异:

Vulkan版

查询扩展

创建句柄对象

录制命令到命令缓冲

提交命令缓冲执行

手动销毁对象

GLES版

查询扩展

设置参数

直接调用函数执行

自动资源回收

方面 GLES 版 Vulkan 版
查询接口 HMS_XEG_GetString HMS_XEG_EnumerateDeviceExtensionProperties
参数设置 HMS_XEG_AdaptiveVRSParameter 逐个设置 通过结构体传递
执行方式 直接调用 录制到命令缓冲
资源管理 无显式销毁 需要手动创建和销毁
对象模型 XEG_AdaptiveVRS 句柄

Vulkan 版的代码量确实比 GLES 版多一些,但换来的是更灵活的命令管理和更好的多线程支持。

使用建议

  1. 对象生命周期管理:创建参数变化时需要重建对象。建议用一个标志位来跟踪参数是否变化。

  2. 命令缓冲要提交HMS_XEG_CmdDispatchAdaptiveVRS 只是录制命令,记得在合适的时候提交命令缓冲。

  3. 描述信息每帧更新XEG_AdaptiveVRSDescription 里的信息每一帧都需要更新,因为着色率图是根据当前帧的输入动态计算的。

  4. 错误检查:创建函数返回 VkResult,记得检查返回值是否为 VK_SUCCESS

Vulkan 版自适应 VRS 适合那些已经用 Vulkan 做渲染引擎的项目。虽然接入成本比 GLES 版高一些,但能更好地融入 Vulkan 的渲染管线。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐