Query instance extensions

Before we can do anything else, we should query the layers and extensions the current implementation supports, and compare them to the list of extensions and layers we need.

The first thing we should do is query all instance layers and extensions.

uint32_t instanceLayerCount                   = 0;
uint32_t instanceExtensionCount               = 0;
VkLayerProperties* instanceLayerProps         = NULL;
VkExtensionProperties* instanceExtensionProps = NULL;

result = vkEnumerateInstanceLayerProperties ( &instanceLayerCount, NULL );
if ( result != VK_SUCCESS )
    RETURN_ERROR(-1,"vkEnumerateInstanceLayerProperties failed (0x%08X)", (uint32_t)result);

instanceLayerProps = _alloca ( instanceLayerCount * sizeof ( VkLayerProperties ) );
result = vkEnumerateInstanceLayerProperties ( &instanceLayerCount, instanceLayerProps );
if ( result != VK_SUCCESS )
    RETURN_ERROR(-1,"vkEnumerateInstanceLayerProperties failed (0x%08X)", (uint32_t)result);

result = vkEnumerateInstanceExtensionProperties ( NULL, &instanceExtensionCount, NULL );
if ( result != VK_SUCCESS )
    RETURN_ERROR(-1,"vkEnumerateInstanceExtensionProperties failed (0x%08X)", (uint32_t)result);

instanceExtensionProps = _alloca ( instanceExtensionCount * sizeof ( VkExtensionProperties ) );
result = vkEnumerateInstanceExtensionProperties ( NULL, &instanceExtensionCount, instanceExtensionProps );
if ( result != VK_SUCCESS )
    RETURN_ERROR(-1,"vkEnumerateInstanceExtensionProperties failed (0x%08X)", (uint32_t)result);

Specifying NULL as the output in vkEnumerateInstanceLayerProperties and vkEnumerateInstanceExtensionProperties output field will allow you to just query the amount of layers and extensions supported by the system. When the amount of layers and extensions are known, we allocate memory for the query results and specify that block of memory as the output for the amount of entries previously queried.

The first parameter of vkEnumerateInstanceExtensionProperties is NULL in our case, but allows querying extensions tied to a specific layer.

For debugging purposes, it can be helpful to print the data from the layer and extension queries.

printf ( "Instance layers (%u): [short]\n", instanceLayerCount );
for ( uint32_t i = 0; i < instanceLayerCount; i++ )
{
    printf (
        " - %s\n"
        , instanceLayerProps[i].layerName
    );
}
printf ( "Instance layers (%u): [long]\n", instanceLayerCount );
for ( uint32_t i = 0; i < instanceLayerCount; i++ )
{
    printf (
        " - name:    %s\n"
        "   specVer: %u.%u.%u\n"
        "   implVer: %u\n"
        "   desc:    %s\n"
        , instanceLayerProps[i].layerName,
        VK_VERSION_MAJOR(instanceLayerProps[i].specVersion),
        VK_VERSION_MINOR(instanceLayerProps[i].specVersion),
        VK_VERSION_PATCH(instanceLayerProps[i].specVersion),
        instanceLayerProps[i].implementationVersion,
        instanceLayerProps[i].description
    );
}

printf ( "Instance extensions (%u): [short]\n", instanceExtensionCount );
for ( uint32_t i = 0; i < instanceExtensionCount; i++ )
{
    printf (
        " - %s\n"
        , instanceExtensionProps[i].extensionName
    );
}
printf ( "Instance extensions (%u): [long]\n", instanceExtensionCount );
for ( uint32_t i = 0; i < instanceExtensionCount; i++ )
{
    printf (
        " - name: %s\n"
        "   ver:  %u.%u.%u\n"
        , instanceLayerProps[i].layerName,
        VK_VERSION_MAJOR(instanceLayerProps[i].specVersion),
        VK_VERSION_MINOR(instanceLayerProps[i].specVersion),
        VK_VERSION_PATCH(instanceLayerProps[i].specVersion)
    );
}

With the extensions and layers listed, we should now match the layers we require to the ones we have on the system, to make sure all layers we depend upon function.

It can be helpful to have a define or other setting which allows quick inclusion of debug layers. Since for this example we are exclusively interested in debug layers, this is included under the BARE_AS_CAN_BE define.

////////////////////////////////////////
// Make sure all required extensions and layers are present

uint32_t failedLayerExtensionCount = 0;
#if !BARE_AS_CAN_BE
for ( uint32_t i = 0; i < STATIC_ARRAY_SIZE(RequiredInstanceLayers); i++ )
{
    uint32_t found = 0;
    for ( uint32_t j = 0; j < instanceLayerCount; j++ )
    {
        if ( strcmp ( RequiredInstanceLayers[i], instanceLayerProps[j].layerName ) == 0 )
        {
            found = 1;
            break;
        }
    }
    if ( !found )
    {
        LOG("ERROR", "Could not find required instance layer \"%s\"", RequiredInstanceLayers[i] );
        failedLayerExtensionCount++;
    }
}
#endif

for ( uint32_t i = 0; i < STATIC_ARRAY_SIZE(RequiredInstanceExtensions); i++ )
{
    uint32_t found = 0;
    for ( uint32_t j = 0; j < instanceExtensionCount; j++ )
    {
        if ( strcmp ( RequiredInstanceExtensions[i], instanceExtensionProps[j].extensionName ) == 0 )
        {
            found = 1;
            break;
        }
    }
    if ( !found )
    {
        LOG("ERROR", "Could not find required instance extension \"%s\"", RequiredInstanceExtensions[i] );
        failedLayerExtensionCount++;
    }
}

if ( failedLayerExtensionCount )
    RETURN_ERROR(-1,"Not all required instance layers and extensions were found; Aborting...");

It is recommended to implement a hashmap or similar structure for improved time complexity in matching layers the application requires to the ones the system provides. As time progresses, the list of layers and extensions might grow to a large list, and matching might take longer. Since this is only needed during initialization, however, this is not a high priority.