Describe the bug
During Vulkan layer version negotiation, if the layer does not support an interface version, it will return VK_ERROR_INITIALIZATION_FAILED during the call to vkNegotiateLoaderLayerInterfaceVersion
If the layer receiving the call no longer supports the interface version provided by the loader (due to deprecation), then it should report a VK_ERROR_INITIALIZATION_FAILED error. Otherwise it sets the value pointed by "loaderLayerInterfaceVersion" to the latest interface version supported by both the layer and the loader and returns VK_SUCCESS. [Source]dead code
Furthermore, when VK_ERROR_INITIALIZATION_FAILED is returned, the loader should not load the layer.
If the loader receives VK_ERROR_INITIALIZATION_FAILED instead of VK_SUCCESS, then the loader will treat the layer as unusable and will not load it. In this case, the application will not see the layer during enumeration. Note that the loader is currently backwards compatible with all layer interface versions, so a layer should not be able to request a version older than what the loader supports. [Source]
This is not what the loader does. Instead, it tries to fallback to finding vkGetInstanceProcAddr directly in the shared library.
The layer negotiation function is called in loader_get_layer_interface_version(). The comments here correctly indicate, that if negotiation fails, the loader should fail loading the Layer.
if (result != VK_SUCCESS) {
// Layer no longer supports the loader's latest interface version so
// fail loading the Layer
return false;
}
This function is called for each activated layer in loader_create_instance_chain(). If the negotiation interface is found and returns success, the interface version and vkGet*ProcAddr functions are updated (there is actually a useless check in loader_get_layer_interface_version, because negotiate_interface cannot be null).
Should negotiation fail however, the layer is not skipped, but instead falls back to the traditional layer loading method which directly fetched the vkGet*ProcAddr functions from the layer.
PFN_vkNegotiateLoaderLayerInterfaceVersion negotiate_interface = NULL;
bool functions_in_interface = false;
if (!layer_prop->functions.str_negotiate_interface || strlen(layer_prop->functions.str_negotiate_interface) == 0) {
negotiate_interface = (PFN_vkNegotiateLoaderLayerInterfaceVersion)loader_platform_get_proc_address(
lib_handle, "vkNegotiateLoaderLayerInterfaceVersion");
} else {
negotiate_interface = (PFN_vkNegotiateLoaderLayerInterfaceVersion)loader_platform_get_proc_address(
lib_handle, layer_prop->functions.str_negotiate_interface);
}
// If we can negotiate an interface version, then we can also
// get everything we need from the one function call, so try
// that first, and see if we can get all the function pointers
// necessary from that one call.
if (NULL != negotiate_interface) {
layer_prop->functions.negotiate_layer_interface = negotiate_interface;
VkNegotiateLayerInterface interface_struct;
if (loader_get_layer_interface_version(negotiate_interface, &interface_struct)) {
// Go ahead and set the properties version to the
// correct value.#include <vulkan/vk_layer.h>
__attribute__((visibility("default")))
VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface*) {
return VK_ERROR_INITIALIZATION_FAILED;
}
layer_prop->interface_version = interface_struct.loaderLayerInterfaceVersion;
// If the interface is 2 or newer, we have access to the
// new GetPhysicalDeviceProcAddr function, so grab it,
// and the other necessary functions, from the
// structure.
if (interface_struct.loaderLayerInterfaceVersion > 1) {
cur_gipa = interface_struct.pfnGetInstanceProcAddr;
cur_gdpa = interface_struct.pfnGetDeviceProcAddr;
cur_gpdpa = interface_struct.pfnGetPhysicalDeviceProcAddr;
if (cur_gipa != NULL) {
// We've set the functions, so make sure we
// don't do the unnecessary calls later.
functions_in_interface = true;
}
}
}
}
if (!functions_in_interface) {
if ((cur_gipa = layer_prop->functions.get_instance_proc_addr) == NULL) {
if (layer_prop->functions.str_gipa == NULL || strlen(layer_prop->functions.str_gipa) == 0) {
cur_gipa =
(PFN_vkGetInstanceProcAddr)loader_platform_get_proc_address(lib_handle, "vkGetInstanceProcAddr");
layer_prop->functions.get_instance_proc_addr = cur_gipa;
if (NULL == cur_gipa) {
loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_LAYER_BIT, 0,
"loader_create_instance_chain: Failed to find \'vkGetInstanceProcAddr\' in layer \"%s\"",
layer_prop->lib_name);
continue;
}
} else {
cur_gipa = (PFN_vkGetInstanceProcAddr)loader_platform_get_proc_address(lib_handle,
layer_prop->functions.str_gipa);
if (NULL == cur_gipa) {
loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_LAYER_BIT, 0,
"loader_create_instance_chain: Failed to find \'%s\' in layer \"%s\"",
layer_prop->functions.str_gipa, layer_prop->lib_name);
continue;
}
}
}
}
This is problematic, because if a layer actually exports these functions (for whatever reason), the layer will still be loaded, even if explicitly disabled by failing negotiation.
Additionally, some layers can use the negotiation interface as a sort of "health check", checking for vital files or the application context it's negotiating in (or other things). For implicit layers, this will cause the following two confusing log messages to appear in the loader log (such as when running vulkaninfo):
ERROR: [Loader Message] Code 0 : loader_create_instance_chain: Failed to find 'vkGetInstanceProcAddr' in layer "liblsfg-vk-layer.so"
ERROR: [Loader Message] Code 0 : loader_create_device_chain: Failed to find 'vkGetInstanceProcAddr' in layer "liblsfg-vk-layer.so". Skipping layer.
Ideally the negotiation behavior is corrected and the layer fails to load as the document suggests. At the very least, the two messages should be silenced as they will only cause confusion. Perhaps an additional INFO (not ERROR) message should be added if negotiation fails?
To Reproduce
#include <vulkan/vk_layer.h>
__attribute__((visibility("default")))
VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface*) {
return VK_ERROR_INITIALIZATION_FAILED;
}
Compiled with clang++ -shared ./src/entrypoint.cpp and set up as an implicit layer. Then typing vulkaninfo should show the log message (and confirm the behavior).
Describe the bug
During Vulkan layer version negotiation, if the layer does not support an interface version, it will return
VK_ERROR_INITIALIZATION_FAILEDduring the call tovkNegotiateLoaderLayerInterfaceVersionFurthermore, when
VK_ERROR_INITIALIZATION_FAILEDis returned, the loader should not load the layer.This is not what the loader does. Instead, it tries to fallback to finding
vkGetInstanceProcAddrdirectly in the shared library.The layer negotiation function is called in
loader_get_layer_interface_version(). The comments here correctly indicate, that if negotiation fails, the loader should fail loading the Layer.This function is called for each activated layer in
loader_create_instance_chain(). If the negotiation interface is found and returns success, the interface version andvkGet*ProcAddrfunctions are updated (there is actually a useless check inloader_get_layer_interface_version, becausenegotiate_interfacecannot be null).Should negotiation fail however, the layer is not skipped, but instead falls back to the traditional layer loading method which directly fetched the
vkGet*ProcAddrfunctions from the layer.This is problematic, because if a layer actually exports these functions (for whatever reason), the layer will still be loaded, even if explicitly disabled by failing negotiation.
Additionally, some layers can use the negotiation interface as a sort of "health check", checking for vital files or the application context it's negotiating in (or other things). For implicit layers, this will cause the following two confusing log messages to appear in the loader log (such as when running
vulkaninfo):Ideally the negotiation behavior is corrected and the layer fails to load as the document suggests. At the very least, the two messages should be silenced as they will only cause confusion. Perhaps an additional INFO (not ERROR) message should be added if negotiation fails?
To Reproduce
Compiled with
clang++ -shared ./src/entrypoint.cppand set up as an implicit layer. Then typingvulkaninfoshould show the log message (and confirm the behavior).