Introduction to extensions

While implementing a Vulkan application, the very first thing that a developer may need to do, and be interested in, is seeing the extended features, functionalities, and capabilities offered by the API. These allow them to gather vital information that can be used to report errors, debug, and trace commands; they can also be used for validation purposes. Vulkan makes use of layers and extensions to expose these additional functionalities:

  • Layers: Layers get hooked up with the existing Vulkan APIs and insert themselves in the chain of Vulkan commands that are associated with the specified layer. It's commonly used for validating the development process. For example, the driver need not check the supplied parameters in the Vulkan API; it's the layer's responsibility to validate whether the incoming parameter is correct or not.
  • Extensions: Extensions provide extended functionality or features, which may or may not be part of the standard specification. The extension could either be a part of the instance or the device. The extension commands cannot be linked statically; they are queried first and then linked dynamically to the function pointers. These function pointers may already be defined in vulkan.h for the registered extension along with the necessary data structures and enumerations.

An extension can be categorized into two types:

  • Instance-based: This represents global functionalities that are independent of any device and can be accessible without any VkDevice
  • Device-based: Here, the extensions are very specific to a device and require a valid handle of the device to operate on and expose the special functionalities

Note

It is suggested that layers and extensions should be enabled during the development phase of an application and turned off at the production stage when the product is expected for release. Turning off the extensions and layers at the production stage allows the application to save unnecessary validation overheads, thus offering higher performance.

Before we get started with application programming, let's take a look at which user-defined classes are used by the sample and what their responsibilities are:

  • Main program: This is an entry point for Hello World!!! It is the application that contains the main() function. The program control logic is built within this file (main.cpp).
  • Headers.h: This is the single place where all the headers are included; we will put our Vulkan headers here.
  • VulkanLayerAndExtension: This class is implemented in VulkanLayerAndExtension.h/.cpp and provides layer- and extension-based functionalities for the instance and device. It also offers debugging capabilities.
  • VulkanInstance: This class creates the Vulkan instance object and is helpful during initialization. It's implemented in VulkanInstance.h/.cpp.
  • VulkanDevice: VulkanDevice.h/.cpp is responsible for creating the logical and physical devices. Each physical device is capable of exposing one or more queues. This class also manages a device's queue and its respective properties.

Querying layers and extensions

In this section, we will implement the main, VulkanApplication, and VulkanLayerAndExtension classes. Now we will begin our Vulkan programming. We'll start by querying the advertised Vulkan layers. Refer to the following instructions to implement this:

  • The very first thing required for Vulkan programming is to add <vulkan/vulkan.h> to the header file Header.h. It contains the most commonly used Vulkan APIs and structure declarations.
  • Create the VulkanLayerAndExtension class and declare the function and variables, as specified in the highlighted part of the following code. Please refer to the inline comments for more details:
      struct LayerProperties { 
         VkLayerProperties                   properties; 
         vector<VkExtensionProperties>       extensions; 
      }; 
 
 
      class VulkanLayerAndExtension{ 
   
        // Layers and corresponding extension list 
         std::vector<LayerProperties> // Instance/global 
         layergetInstanceLayerProperties(); 
 
        // Global extensions 
         VkResult getExtensionProperties(LayerProperties  
                     &layerProps, VkPhysicalDevice* gpu = NULL); 
 
        // Device based extensions 
         VkResult getDeviceExtensionProperties(VkPhysicalDevice*gpu);  
      }; 
  • Upon application startup, the getInstanceLayerProperties() helper function queries either instance or global layers. It gets the total count of the layers and stores all of the layer information in a VkLayerProperties vector called layerProperties. Both the operations (count and store) are done by calling vkEnumerateInstanceLayerProperties() twice. For the first time, calling the API with the second argument as NULL returns the layer count in the first argument, instanceLayerCount. In the second instance, instead of providing the second argument as NULL, pass it as an array/vector of VkLayerProperties and fetch detailed property information into it.

Note

Most enumerated APIs under Vulkan are used to perform more than one functionality, based on the arguments supplied. Just now, we have seen that the vkEnumerateInstanceLayerProperties API is not only used to retrieve the layer count (by supplying a NULL argument), but also to get the array of layers (by supplying an array of data structures) that contains information.

Here's the syntax of the preceding code:

VkResult VulkanLayerAndExtension::getInstanceLayerProperties() 
{ 
   // Stores number of instance layers 
   uint32_t instanceLayerCount; 
   // Vector to store layer properties 
          
   std::vector<VkLayerProperties>      layerProperties;         
   // Check Vulkan API result status 
   VkResult result;  
 
   // Query all the layers 
   do { 
         result = vkEnumerateInstanceLayerProperties 
                     (&instanceLayerCount, NULL); 
 
         if (result) 
               return result; 
 
         if (instanceLayerCount == 0) 
               return VK_INCOMPLETE; // return fail 
 
         layerProperties.resize(instanceLayerCount); 
         result = vkEnumerateInstanceLayerProperties 
               (&instanceLayerCount, layerProperties.data()); 
   } while (result == VK_INCOMPLETE); 
 
   // Query all the extensions for each layer and store it. 
   std::cout << "
Instanced Layers" << std::endl; 
   std::cout << "===================" << std::endl; 
   for (auto globalLayerProp: layerProperties) { 
 
         // Print layer name and its description 
         std::cout <<"
"<< globalLayerProp.description << 
                     "
	|
	|---[Layer Name]--> " <<  
                     globalLayerProp.layerName <<"
"; 
 
         LayerProperties layerProps; 
         layerProps.properties = globalLayerProp; 
 
         // Get Instance level extensions for
 
        // corresponding layer properties 
         result = getExtensionProperties(layerProps); 
 
         if (result){ 
             continue; 
         } 
 
         layerPropertyList.push_back(layerProps); 
 
         // Print extension name for each instance layer 
         for (auto j : layerProps.extensions){ 
             std::cout << "		|
		|--- 
             [Layer Extension]--> " << j.extensionName << "
"; 
         } 
 
   } 
   return result; 
} 
 

The following is the syntax of vkEnumerateInstanceLayerProperties():

VkResult vkEnumerateInstanceLayerProperties (  
               uint32_t*         pPropertyCount, 
               VkLayerProperties* pProperties); 

The following table describes the vkEnumerateInstanceLayerProperties() API fields:

Parameters

Description

pPropertyCount

This variable represents the number of layers at the instance level. This variable works as an input or output variable, depending upon the value passed to pProperties.

pProperties

This variable can take two values. When specified as NULL, the API returns the layer count in pPropertyCount with the total number of layers. When used as an array, the API retrieves the information of the layer properties in the same array.

Once we retrieve the layer property information for each layer, we'll use it to iterate through all the layers in order to query the extensions exposed by each layer. We'll do this by calling our user-defined helper function getExtensionProperties().

Upon the successful execution of the layer and their extensions, you'll see the following output on the console:

LunarG debug layer 
        |---[Layer Name]--> VK_LAYER_LUNARG_api_dump 
 
LunarG Validation Layer 
        |---[Layer Name]--> VK_LAYER_LUNARG_core_validation 
                |---[Layer Extesion]--> VK_EXT_debug_report 
 
LunarG Standard Validation Layer 
        |---[Layer Name]--> VK_LAYER_LUNARG_standard_validation 
 
LunarG Validation Layer 
        |---[Layer Name]--> VK_LAYER_LUNARG_device_limits 
                |---[Layer Extesion]--> VK_EXT_debug_report 

Each layer may be capable of supporting one or more extensions. The getExtensionProperties() function first enumerates the layers to get the number of extensions exposed. Then, it stores the extension properties information in the LayerProperties data structures using the vkEnumerate-InstanceExtensionProperties() API. This whole process is very similar to the layer enumeration; refer to getInstanceLayerProperties() for more information on the last step. The getExtensionProperties() function queries the extensions for both the instance and device:

// This function retrieves extension and its 

// properties at instance and device level. 
   
// Pass a valid physical device pointer (gpu) to retrieve

// device level extensions, otherwise use NULL to 

// retrieve extension specific to instance level. 
VkResult VulkanLayerAndExtension::getExtensionProperties 
   (LayerProperties &layerProps, VkPhysicalDevice* gpu) 
{ 
   // Stores number of extension per layer 
   uint32_t    extensionCount;                                 
   VkResult    result;                                        
   // Name of the layer  
   char*       layerName = layerProps.properties.layerName;  
 
   do { 
         // Get the total number of extension in this layer 
         if(gpu){ 
             result = vkEnumerateDeviceExtensionProperties 
                           (*gpu, layerName, 
                           &extensionCount, NULL); 
         } 
         else{ 
             result = vkEnumerateInstanceExtensionProperties 
                     (layerName, &extensionCount, NULL); 
         } 
 
         if (result || extensionCount == 0) 
               continue; 
 
         layerProps.extensions.resize(extensionCount); 
 
         // Gather all extension properties  
         if (gpu){ 
                   result = vkEnumerateDeviceExtensionProperties 
                         (*gpu, layerName, &extensionCount,  
                         layerProps.extensions.data()); 
         } 
         else{ 
             result = vkEnumerateInstanceExtensionProperties 
                         (layerName, &extensionCount,  
                         layerProps.extensions.data()); 
         } 
 
   } while (result == VK_INCOMPLETE); 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.42.243