Calling vkEnumerateDeviceExtensionProperties “twic

2019-05-26 03:38发布

问题:

From the man page for vkEnumerateDeviceExtensionProperties,

vkEnumerateDeviceExtensionProperties retrieves properties for extensions on a physical device whose handle is given in physicalDevice. To determine the extensions implemented by a layer set pLayerName to point to the layer’s name and any returned extensions are implemented by that layer. Setting pLayerName to NULL will return the available non-layer extensions. pPropertyCount must be set to the size of the VkExtensionProperties array pointed to by pProperties. The pProperties should point to an array of VkExtensionProperties to be filled out or null. If null, vkEnumerateDeviceExtensionProperties will update pPropertyCount with the number of extensions found. The definition of VkExtensionProperties is as follows:

(emphasis mine). It seems in the current implementation (Window SDK v1.0.13), pPropertyCount is updated with the number of extensions, regardless of whether pProperties is null or not. However, the documentation doesn't appear to be explicit on what happens in this situation.

Here's an example, of why having such a feature is 'nicer':

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);
    //...
}

My question is: am I depending on unsupported functionality by doing this, or is this somehow advertised somewhere else in the documentation?

回答1:

From the latest spec:

For both vkEnumerateInstanceExtensionProperties and vkEnumerateDeviceExtensionProperties, if pProperties is NULL, then the number of extensions properties available is returned in pPropertyCount. Otherwise, pPropertyCount must point to a variable set by the user to the number of elements in the pProperties array, and on return the variable is overwritten with the number of structures actually written to pProperties. If pPropertyCount is less than the number of extension properties available, at most pPropertyCount structures will be written. If pPropertyCount is smaller than the number of extensions available, VK_INCOMPLETE will be returned instead of VK_SUCCESS, to indicate that not all the available properties were returned.

So your approach is correct, even though it's a bit wasteful on memory. Similar functions returning arrays also behave like this.

Also note that since 1.0.13, device layers are deprecated. All instance layers are able to intercept commands to both the instance and the devices created from it.



回答2:

Most Vulkan commands relays in double calls:

  1. First call to get count number of returning structures or handles;
  2. Second call to pass an properly sized array to get back requested structures/handle. In this second call, the count parameter tells the size of your array.

If , in second step, you get VkResult::VK_INCOMPLETE result then you passed an array too short to get all objects back. Note VK_INCOMPLETE is not an error, it is a partial success (2.6.2 Return Codes ... "All successful completion codes are non-negative values. ")

Your Question :

Am I depending on unsupported functionality by doing this, or is this somehow advertised somewhere else in the documentation?

You proposed create a big array before calling the function, to avoid a call Vulkan function twice.

My reply: Yes, and you are doing a bad design decision by "guessing" the array size.

Please, don't get me wrong. I strongly agree with you that is annoying to call same function twice, but you can solve it by wrapping those sort functions with a more programmer friendly behaviour.

I'll use another Vulkan function, just to illustrate it. Let say you want to avoid double call to :

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

A possible solution would be write the sweet wrap function:

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        
}

That is my two cents about the double call to Vulkan functions. It is a naive implementation and may not work for all cases! Note you must create the vector BEFORE you call the wrapping function.

Good Luck!



标签: c++ vulkan