c++environment-variablesvulkanvalidation-layers

How to set validation layers from within program rather than env var?


The official vulkan documentation claims:

Applications may programmatically activate layers via the vkCreateInstance() entry point.

And this is given as alternative to setting an environment variable. However, nothing else is said in this section about how to set them. We can read the official docs of VkCreateInstance which say nothing about validaiton layers, so we can look at the CreateInfo instead. In here the only relevant field seems to be ppEnabledLayerNames.

Based on everything so far, one might think that passing the correct strings to the create info is enough to set the layers. So one might do this:

    const std::vector<const char*> VALIDATION_LAYERS = {"VK_LAYER_KHRONOS_validation"};

    // Create Vulkan instance to communicate with the loader
    vk::InstanceCreateInfo create_info = {};
    create_info.pApplicationInfo = &program_info,
    create_info.enabledLayerCount = static_cast<uint32_t>(VALIDATION_LAYERS.size()),
    create_info.ppEnabledLayerNames = VALIDATION_LAYERS.data(),
    create_info.enabledExtensionCount = static_cast<uint32_t>(required_extensions.size()),
    create_info.ppEnabledExtensionNames = required_extensions.data();

    auto [result, instance] = vk::createInstanceUnique(create_info);

I am running the above snippet within a larger application. If I manually set the env var as described in the docs, it runs. If I however do not set the env var and instead rely only on the above snippet, my program crashes on instance creation.

Since the docs explicitly say you can set things programmatically but don't seem to say how, what should you do to avoid relying on the env var?

To be more specific, the error I get when running the snippet occurs in the hpp header, an assertion fails:

typename vk::ResultValueType<vk::UniqueHandle<Type, Dispatch> >::type vk::createResultValue(vk::Result, T&, const char*, const typename vk::UniqueHandleTraits<Type, Dispatch>::deleter&) [with T = vk::Instance; D = vk::DispatchLoaderStatic; typename vk::ResultValueType<vk::UniqueHandle<Type, Dispatch> >::type = vk::ResultValue<vk::UniqueHandle<vk::Instance, vk::DispatchLoaderStatic> >; typename vk::UniqueHandleTraits<Type, Dispatch>::deleter = vk::ObjectDestroy<vk::NoParent, vk::DispatchLoaderStatic>]: Assertion `result == Result::eSuccess' failed.

Analyzing the stack with gdb shows this occurs on instance creation. The specific error code that makes the assertion fail is vk::Result::eErrorLayerNotPresent.

And as an added piece of information, calling: auto [result, availableLayers] = vk::enumerateInstanceLayerProperties();

Immediately before calling instance creation returns an empty array.


Solution

  • Simply VkInstanceCreateInfo::ppEnabledLayerNames enables layers.

    Layers have to be available before they can be enabled. That can be determined by vkEnumerateInstanceLayerProperties.

    Obviously, Vulkan is not magic and the Loader needs to know where to look for extensions. That system is outlined in LoaderAndLayerInterface.md.

    Basically on Windows it looks in canonical path in registry. On Linuxes it looks in canonical paths in filesystem. These will be set by installable versions of the SDK. Or VK_LAYER_PATH env variable can override the default paths.