Vulkan 下怎么查 XEngine 支持哪些特性?用枚举扩展接口

在 GLES 环境下,你用 HMS_XEG_GetString 一行代码就能查到所有支持的 XEngine 扩展。但到了 Vulkan 环境下,查询方式变了——你需要用 HMS_XEG_EnumerateDeviceExtensionProperties 接口。这篇文章就来详细讲讲这个 Vulkan 版的扩展查询接口怎么用。

为什么要用不同的接口

GLES 和 Vulkan 的设计理念不同。GLES 是"查字符串"风格——返回一个空格分隔的扩展名列表,你用字符串搜索来判断支持什么。Vulkan 是"查结构体"风格——返回一个结构体数组,每个结构体描述一个扩展的属性。

XEngine 遵循了各自的惯例,所以 GLES 版用字符串查询,Vulkan 版用结构体查询。

接口详解

HMS_XEG_EnumerateDeviceExtensionProperties 的函数签名是这样的:

VKAPI_ATTR VkResult VKAPI_CALL HMS_XEG_EnumerateDeviceExtensionProperties(
    VkPhysicalDevice physicalDevice,
    uint32_t *pPropertyCount,
    XEG_ExtensionProperties *pProperties
);

三个参数:

physicalDevice:当前使用的 Vulkan 物理设备。就是你通过 vkEnumeratePhysicalDevices 拿到的那个。

pPropertyCount:指向数量的指针。这个参数的行为取决于 pProperties 是否为 NULL:

  • 如果 pProperties 为 NULL:函数会把支持的扩展数量写入 *pPropertyCount
  • 如果 pProperties 不为 NULL:*pPropertyCount 表示你提供的数组大小

pProperties:指向 XEG_ExtensionProperties 数组的指针。如果为 NULL,函数只返回数量。

返回值是 VkResult

  • VK_SUCCESS:查询成功,所有扩展信息都返回了
  • VK_INCOMPLETE:你提供的数组太小,只返回了部分信息

XEG_ExtensionProperties 结构体

每个扩展的信息通过 XEG_ExtensionProperties 结构体返回:

struct XEG_ExtensionProperties {
    char extensionName[XEG_MAX_EXTENSION_NAME_SIZE];  // 扩展名称
    // ... 可能还有其他字段
};

XEG_MAX_EXTENSION_NAME_SIZE 的值是 256,意味着扩展名最长 255 个字符(加上结尾的 \0)。

典型用法:两步查询法

Vulkan 风格的查询通常是两步走:先问有多少个,再一个个取回来。下面的流程图展示了完整的查询流程:

调用查询接口 pProperties=NULL

获取扩展数量propertyCount

propertyCount是否为0?

没有可用扩展

分配propertyCount大小的数组

再次调用查询接口获取扩展列表

返回值是否VK_INCOMPLETE?

数组太小, 重新分配并查询

遍历扩展列表

用strcmp匹配目标扩展名

找到目标扩展

初始化对应XEngine特性

// 第一步:查询扩展数量
uint32_t propertyCount = 0;
VkResult result = HMS_XEG_EnumerateDeviceExtensionProperties(
    physicalDevice, &propertyCount, NULL
);

if (result != VK_SUCCESS || propertyCount == 0) {
    // 没有可用的 XEngine 扩展
    return;
}

// 第二步:分配空间并查询扩展列表
XEG_ExtensionProperties* properties = (XEG_ExtensionProperties*)malloc(
    sizeof(XEG_ExtensionProperties) * propertyCount
);

result = HMS_XEG_EnumerateDeviceExtensionProperties(
    physicalDevice, &propertyCount, properties
);

if (result != VK_SUCCESS) {
    free(properties);
    return;
}

// 第三步:遍历扩展列表,查找你需要的特性
for (uint32_t i = 0; i < propertyCount; i++) {
    printf("XEngine Extension: %s\n", properties[i].extensionName);
}

free(properties);

检查具体特性是否支持

拿到扩展列表之后,你可以检查你需要的特性是否在里面。下面的流程图展示了 XEngine 支持的主要扩展特性:

XEngine扩展特性

图像增强类

光照渲染类

计算加速类

XEG_spatial_upscale: 空域超分

XEG_temporal_upscale: 时域超分

XEG_adaptive_vrs: 自适应着色

XEG_rtgi: 全局光照

XEG_rt_shadow_ao: 阴影+环境光遮蔽

XEG_rt_reflection: 光线追踪反射

XEG_hps_radix_sort: 高性能基数排序

XEngine 定义了一系列扩展名宏,方便你使用:

// 空域 GPU 超分
if (strcmp(properties[i].extensionName, XEG_SPATIAL_UPSCALE_EXTENSION_NAME) == 0) {
    // 支持空域 GPU 超分
}

// 时域 AI 超分
if (strcmp(properties[i].extensionName, XEG_TEMPORAL_UPSCALE_EXTENSION_NAME) == 0) {
    // 支持时域 AI 超分
}

// 自适应 VRS
if (strcmp(properties[i].extensionName, XEG_ADAPTIVE_VRS_EXTENSION_NAME) == 0) {
    // 支持自适应 VRS
}

// 光线追踪全局光照
if (strcmp(properties[i].extensionName, XEG_RTGI_EXTENSION_NAME) == 0) {
    // 支持 RTGI
}

// 光线追踪阴影和环境光遮蔽
if (strcmp(properties[i].extensionName, XEG_RT_SHADOW_AO_EXTENSION_NAME) == 0) {
    // 支持 RT ShadowAO
}

// 光线追踪反射
if (strcmp(properties[i].extensionName, XEG_RT_REFLECTION_EXTENSION_NAME) == 0) {
    // 支持 RT Reflection
}

// 高性能基数排序
if (strcmp(properties[i].extensionName, XEG_HPS_RADIX_SORT_EXTENSION_NAME) == 0) {
    // 支持 HPS 基数排序
}

这些扩展名宏的值分别是:

宏名
XEG_SPATIAL_UPSCALE_EXTENSION_NAME "XEG_spatial_upscale"
XEG_TEMPORAL_UPSCALE_EXTENSION_NAME "XEG_temporal_upscale"
XEG_ADAPTIVE_VRS_EXTENSION_NAME "XEG_adaptive_vrs"
XEG_RTGI_EXTENSION_NAME "XEG_rtgi"
XEG_RT_SHADOW_AO_EXTENSION_NAME "XEG_rt_shadow_ao"
XEG_RT_REFLECTION_EXTENSION_NAME "XEG_rt_reflection"
XEG_HPS_RADIX_SORT_EXTENSION_NAME "XEG_hps_radix_sort"

封装一个辅助函数

在实际项目里,你可能会频繁查询某个特定扩展是否支持。建议封装一个辅助函数:

bool isXEngineExtensionSupported(VkPhysicalDevice physicalDevice, const char* extensionName) {
    uint32_t propertyCount = 0;
    HMS_XEG_EnumerateDeviceExtensionProperties(physicalDevice, &propertyCount, NULL);

    if (propertyCount == 0) return false;

    XEG_ExtensionProperties* properties = malloc(sizeof(XEG_ExtensionProperties) * propertyCount);
    HMS_XEG_EnumerateDeviceExtensionProperties(physicalDevice, &propertyCount, properties);

    bool found = false;
    for (uint32_t i = 0; i < propertyCount; i++) {
        if (strcmp(properties[i].extensionName, extensionName) == 0) {
            found = true;
            break;
        }
    }

    free(properties);
    return found;
}

// 使用:
if (isXEngineExtensionSupported(physicalDevice, XEG_TEMPORAL_UPSCALE_EXTENSION_NAME)) {
    // 初始化时域超分...
}

当然,更高效的做法是在初始化的时候查询一次,把结果缓存起来,后面直接查缓存。

VK_INCOMPLETE 的处理

如果 HMS_XEG_EnumerateDeviceExtensionProperties 返回 VK_INCOMPLETE,说明你提供的数组太小了,只拿到了部分扩展信息。这种情况一般发生在你第一次查询之后、第二次查询之前,系统支持的扩展数量增加了(理论上不太可能发生,但防御性编程总没错)。

处理方式很简单:用返回的 propertyCount 重新分配空间,再查一次。

和 GLES 版的对比

方面 GLES 版 Vulkan 版
查询函数 HMS_XEG_GetString HMS_XEG_EnumerateDeviceExtensionProperties
返回格式 空格分隔的字符串 结构体数组
判断方式 strstr 字符串搜索 strcmp 精确匹配
物理设备参数 不需要 需要 VkPhysicalDevice

Vulkan 版的查询虽然代码多一点,但更结构化、更不容易出错。字符串搜索有时候会碰到子串匹配的问题(比如 "XEG_spatial" 会匹配到 "XEG_spatial_upscale"),而 strcmp 是精确匹配,不会有这个问题。

使用建议

  1. 初始化时查询一次就够了:扩展列表在运行过程中不会变。在 Vulkan 设备创建之后、特性初始化之前查一次,把结果存起来。

  2. 封装辅助函数:别每次都写一堆查询代码,封装一个 isXEngineExtensionSupported 函数会让代码清爽很多。

  3. 注意物理设备:Vulkan 可能有多个物理设备(比如集成显卡和独立显卡)。确保你查询的是你实际使用的那个物理设备。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐