c++ 调用vkEnumerateDeviceExtensionProperties“两次”-这是必需的吗?

8ftvxx2r  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(184)

man page for vkEnumerateDeviceExtensionProperties
vkEnumerateDeviceExtensionProperties检索物理设备上的扩展的属性,该物理设备的句柄在physicalDevice中给出。要确定由层实现的扩展,请将pLayerName设置为指向该层的名称,并且任何返回的扩展都由该层实现。将pLayerName设置为NULL将返回可用的非层扩展。pPropertyCount必须设置为pProperties指向的VkExtensionProperties数组的大小。pProperties应指向要填写的VkExtensionProperties数组,或null.**如果为null,则vkEnumerateDeviceExtensionProperties将使用找到的扩展数量更新pPropertyCount。**VkExtensionProperties的定义如下:
(强调我的)。似乎在当前的实现(Window SDK v1.0.13)中,pPropertyCount * 会随着扩展的数量而更新,而不管pProperties是否为null。然而,文档似乎没有明确说明在这种情况下会发生什么。
这里有一个例子,为什么有这样一个功能是“更好”:

const uint32_t MaxCount = 1024; // More than you'll ever need
uint32_t ActualCount = MaxCount;
VkLayerProperties layers[MaxCount];
VkResult result = vkEnumerateDeviceLayerProperties(physicalDevice, &ActualCount, layers);
//...

字符串
vs.

uint32_t ActualCount = 0;
VkLayerProperties* layers;
VkResult result = vkEnumerateDeviceLayerProperties(physicalDevice, &ActualCount, nullptr);
if (ActualCount > 0) 
{
    extensions = alloca(ActualCount * sizeof(VkLayerProperties));
    result = vkEnumerateDeviceLayerProperties(physicalDevice, &ActualCount, layers);
    //...
}

**我的问题是:**我这样做是依赖于不受支持的功能,还是在文档的其他地方以某种方式宣传了这一点?

cunj1qz1

cunj1qz11#

关于latest spec
对于vkEnumerateInstanceExtensionProperties和vkEnumerateDeviceExtensionProperties,如果pProperties为NULL,则在pPropertyCount中返回可用扩展属性的数量。否则,pPropertyCount必须指向用户设置的变量,该变量表示pProperties数组中的元素数量,返回时,变量将被实际写入pProperties的结构数覆盖如果pPropertyCount小于可用扩展属性的数量,则最多写入pPropertyCount结构。如果pPropertyCount小于可用扩展的数量,则将返回VK_INCOMPLENT而不是VK_SUCCESS,以指示未返回所有可用属性。
所以你的方法是正确的,尽管它有点浪费内存。类似的函数返回数组也是这样的。
还要注意的是,从1.0.13开始,设备层被弃用了。所有示例层都能够拦截到示例和从示例创建的设备的命令。

4smxwvx5

4smxwvx52#

大多数Vulkan命令以双重调用的方式进行中继:
1.第一次调用获取返回结构或句柄的计数;
1.第二次调用传递一个适当大小的数组以获取请求的结构/句柄。在第二次调用中,count参数告诉数组的大小。
如果在第二步中,你得到了VkResult::VK_INCOMPUTER结果,那么你传递了一个太短的数组,无法返回所有对象。注意VK_INCOMPUTER不是错误,它是部分成功 (2.6.2返回代码.“所有成功的完成代码都是非负值“)
您的问题:
我这样做是依赖于不受支持的功能,还是在文档的其他地方以某种方式宣传了这一点?
您建议在调用函数之前创建一个大数组,以避免两次调用Vulkan函数。
我的回答是:是的,你通过“猜测”数组大小来做一个糟糕的设计决策。
请不要误会我的意思。我强烈同意你的观点,调用同一个函数两次是很烦人的,但是你可以通过用一个更程序员友好的行为来 Package 那些排序函数来解决这个问题。
我将使用另一个Vulkan函数来说明它。假设您希望避免双重调用:

VkResult vkEnumeratePhysicalDevices(
VkInstance                                  instance,
uint32_t*                                   pPhysicalDeviceCount,
VkPhysicalDevice*                           pPhysicalDevices);

字符串
一个可能的解决方案是编写sweet wrap函数:

VkResult getPhysicalDevices(VkInstance instance,  std::vector<VkPhysicalDevice>& container){
   uint32_t count = 0;
   VkResult res = vkEnumeratePhysicalDevices(instance, &count, NULL); // get #count
   container.resize(count); //Removes extra entries or allocates more.
   if (res < 0) // something goes wrong here
         return res;       
   res =  vkEnumeratePhysicalDevices(instance, &count, container.data()); // all entries overwritten.
   return res; // possible OK        
}


这就是我对Vulkan函数的双重调用的看法。**这是一个幼稚的实现,可能不适用于所有情况!**注意,在调用 Package 函数之前,必须创建向量。
祝你好运!

cl25kdpy

cl25kdpy3#

我同意你的观点。重复函数调用是非常烦人的,会导致潜在的错误。我做了这个模板函数来绕过双重调用。
如果你想帮助我改进这段代码,请随意添加注解到gist https://gist.github.com/Pacheco95/e63d24e1a16f2556e0f589da209f9ad9

#ifndef VULKAN_DOUBLE_CALL_WRAPPER_HPP
#define VULKAN_DOUBLE_CALL_WRAPPER_HPP

#include <vulkan/vulkan.h>

#include <tuple>
#include <vector>

namespace engine {

template <typename T>
struct function_traits;

template <typename R, typename... Args>
struct function_traits<R (*)(Args...)> {
  using last_argument_type = typename std::
      tuple_element<sizeof...(Args) - 1, std::tuple<Args...>>::type;
};

template <typename F, typename... Args>
/**
 * This is a helper wrapper function to avoid the doubled calls for some
 * vulkan functions like `vkEnumerateDeviceExtensionProperties`.
 * Instead of repeating this behaviour:
 * \code
 * uint32_t extensionCount;
 * vkEnumerateDeviceExtensionProperties(
 *     device, nullptr, &extensionCount, nullptr
 * );
 * std::vector<VkExtensionProperties> availableExtensions(extensionCount);
 * vkEnumerateDeviceExtensionProperties(
 *     device, nullptr, &extensionCount, availableExtensions.data()
 * );
 * \endcode
 *
 * You can just do this:
 *
 * \code
 * // The vector type will be inferred to std::vector<VkExtensionProperties>
 * std::vector availableExtensions =
 *     vkCall(
 *         vkEnumerateDeviceExtensionProperties,
 *         physicalDevice,
 *         pLayerName);
 * \endcode
 * @tparam F
 * @tparam Args
 * @param func the vkCreate function to be called
 * @param args all arguments of vkCreate except last two
 * @return
 */
auto vkCall(F func, Args&&... args) -> std::vector<typename std::remove_pointer<
    typename function_traits<F>::last_argument_type>::type> {
  using T = typename std::remove_pointer<
      typename function_traits<F>::last_argument_type>::type;

  uint32_t count;
  func(std::forward<Args>(args)..., &count, nullptr);

  std::vector<T> container(count);
  func(std::forward<Args>(args)..., &count, container.data());

  return container;
}

}  // namespace engine

#endif  // VULKAN_DOUBLE_CALL_WRAPPER_HPP

字符串

相关问题