vkEnumerate *的正确两次呼叫模式是什么?

问题描述

我发现了vkEnumerate*的两次呼叫模式的两个变体。我想知道第二种解决方案比第一种解决方案的优点是什么。

一个解决方案:

uint32_t count = 0;
vkEnumerateTs(ts...,&count,nullptr);
std::vector<T> results(count);
auto error = vkEnumerateTs(args...,results.data());

解决方案未考虑到的是vkEnumerateInstanceLayerProperties 并且vkEnumerateInstanceExtensionProperties可以随时更改(请参见Vulkan规范1.2.151的37.4.1):

由于Vulkan以外的动作,可用层的列表可能随时更改 实现,因此可能会使用相同的参数两次调用vkEnumerateInstanceLayerProperties 返回不同的结果,或获取不同的pPropertyCount值或pProperties 内容。创建实例后,为该实例启用的图层将继续 在该实例的生命周期内被启用并有效,即使其中一些实例变为 将来的实例不可用。

(与vkEnumerateInstanceExtensionProperties类似。) 如果count比以前大,那么您将无法获取所有信息。 如果count比以前小,则results的尾部包含无效数据。

注意:其他vkEnumerate*命令的结果似乎是无限的 根据Vulkan规范1.2.151的 2.5.1检索结果的生命周期的生命周期,因此对于其他vkEnumerate*命令,第一个解决方案就足够了:

除非为单个命令另外指定,否则结果为不变;也就是说,只要这些参数本身都保持有效,则通过使用相同参数调用相同命令再次检索它们时,它们将保持不变。

一个问题可以通过扩展上述解决方案来解决 (如果VK_INCOMPLETE小于可用属性数量,则返回count):

// Check if too much space was allocated.
if (error != VK_INCOMPLETE)
{
    results.resize(count);
}

第二个变体(取自 Vulkan-Tools; fvkEnumerateTsinit用作模板的哨兵类型 推导和认值)似乎可以解决一个问题(无法获取所有信息):

// Helper for robustly executing the two-call pattern
template <typename T,typename F,typename... Ts>
auto GetVectorInit(const char *func_name,F &&f,T init,Ts &&... ts) -> std::vector<T> {
    uint32_t count = 0;
    std::vector<T> results;
    VkResult err;
    do {
        err = f(ts...,nullptr);
        if (err) THROW_VK_ERR(func_name,err);
        results.resize(count,init);
        err = f(ts...,results.data());
        results.resize(count);
    } while (err == VK_INCOMPLETE);
    if (err) THROW_VK_ERR(func_name,err);
    return results;
}

但是,即使尝试获取“全部”信息,又有什么用呢? 可用信息可能随时更改?为什么要不断迭代 当您离开时,while循环可获取“完整”信息 while循环返回结果时,可能已经不完整了吗? 更糟糕的是,(理论上)您可能会永远陷入这个while循环中 如果在第一次调用count之后f的值总是较小 大于第二个调用后的值。

我误解了情况吗?第二种变体 有什么优点(我建议进行修改)?

解决方法

好的,这主要是基于观点或审美的问题。我觉得您在考虑一些可能无关紧要的事情。

每个版本本身都没有错。还有另一个通用版本,可以通过估计在堆栈上创建足够大的数组,并且只需调用一次Vulkan查询即可。说到vkEnumerateInstanceLayerProperties,另一种选择是完全跳过它,仅依靠VK_ERROR_LAYER_NOT_PRESENT


查询不是代码中有趣的部分。众所周知,这里要解决的是C API的大部分C ++标准化(std::vector,并试图满足DRY原理),而不是编码有用的东西。

我在这里的论点是,最“聪明”且最令人惊讶的实现是最好的。基本上,您可以快速浏览并说出“是的,这符合我的意图,并且似乎可以正确地涵盖所有危险情况”,而无需三思而后行。

如果您已经获得VK_INCOMPLETE,那么您实际上知道数据已经不完整,那么为什么要返回您知道的事实数据却不完整?好的,这是0.0001%的情况发生的a幸。好的,无论如何以后可能会更改数据。但是问题是相反的。第一版本比第二版本提供了什么?在我看来,它只是通过对情况进行巧妙的分析而跳过处理错误代码之一来尝试变得聪明。通过它得到什么?两行代码少吗?聪明和聪明并不是真正需要的,最好在其他地方使用,并且代码的读者可能不会期望普通代码中有聪明的东西。

无限循环的情况确实是理论上的问题(我应该说不存在)。它将需要恶意驱动程序来主动创建这种情况。在这种情况下,您会有更大的问题。