From 3b71bfe5d70d682f531725491eaf9f9f417daf99 Mon Sep 17 00:00:00 2001 From: Slawomir Cygan Date: Mon, 3 Jul 2017 16:47:52 +0200 Subject: [PATCH] loader: Add new ICD search paths to loader This change extends the functionality of searching for ICD JSONs by adding registry locations specific to given display adapter and software components associated with this display adapter. The exact locations in registry are queried using Windows public PnP Configuration Manager API[1]. This change is required, as previous ICD locations (constant path in "HKLM/Software") may be unreachable for drivers and their installers on Windows RS3[2]. Similar change is being made for OpenCL[2] [1]https://msdn.microsoft.com/en-us/library/windows/hardware/ff549713.aspx [2]https://github.com/KhronosGroup/OpenCL-ICD-Loader/pull/21 --- loader/CMakeLists.txt | 2 +- loader/LoaderAndLayerInterface.md | 21 +++ loader/loader.c | 261 +++++++++++++++++++++++++++++- loader/vk_loader_platform.h | 5 + 4 files changed, 285 insertions(+), 4 deletions(-) diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt index 4a70e39f..18ce4253 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -162,7 +162,7 @@ if (WIN32) # Suppress conflicting libs warning for debug builds. set_target_properties(${API_LOWERCASE}-${MAJOR} PROPERTIES LINK_FLAGS_DEBUG /ignore:4098) set_target_properties(VKstatic.${MAJOR} PROPERTIES OUTPUT_NAME VKstatic.${MAJOR}) - target_link_libraries(${API_LOWERCASE}-${MAJOR} shlwapi) + target_link_libraries(${API_LOWERCASE}-${MAJOR} shlwapi Cfgmgr32) add_dependencies(${API_LOWERCASE}-${MAJOR} generate_helper_files loader_gen_files loader_asm_gen_files) target_link_libraries(VKstatic.${MAJOR} shlwapi) diff --git a/loader/LoaderAndLayerInterface.md b/loader/LoaderAndLayerInterface.md index cf3f4c60..f3d0944b 100644 --- a/loader/LoaderAndLayerInterface.md +++ b/loader/LoaderAndLayerInterface.md @@ -1922,6 +1922,27 @@ the value is 0, then the loader will attempt to load the file. In this case, the loader will open the first and last listings, but not the middle. This is because the value of 1 for vendorb_vk.json disables the driver. +Additionaly the loader will scan through registry keys specific to Display +Adapters and all SoftwareComponents assocatied with these adapters for the +locations of JSON manifest files. These keys are reflecting HKR device hive +created during driver installation and contain configuration for base settings, +including OpenGL/D3D ICD location. + +The Device Adapter and SoftwareComponent key paths should be obtained via PnP +Configuration Manager API. In each of this path, a "VulkanDriverName" (and +"VulkanDriverNameWow" for 32-bit Windows on Windows compatibility) key can be +present, what should look like: +``` + HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Class\{Adapter GUID}\000X\VulkanDriverName + HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Class\{Adapter GUID}\000X\VulkanDriverNameWow + + HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Class\{SoftwareComponent GUID}\000X\VulkanDriverName + HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Class\{SoftwareComponent GUID}\000X\VulkanDriverNameWow +``` +If any of these keys exsist and is of type REG_SZ, the loader will open the JSON +manifest file specified by the key valye. Each value must be a full absolute +path to a JSON manifest file. + The Vulkan loader will open each enabled manifest file found to obtain the name or pathname of an ICD shared library (".DLL") file. diff --git a/loader/loader.c b/loader/loader.c index 468804f5..c75bcf79 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -47,6 +47,12 @@ #include "cJSON.h" #include "murmurhash.h" +#if defined(_WIN32) +#include +#include +#include +#endif + // This is a CMake generated file with #defines for any functions/includes // that it found present. This is currently necessary to properly determine // if secure_getenv or __secure_getenv are present @@ -401,6 +407,245 @@ VKAPI_ATTR VkResult VKAPI_CALL vkSetDeviceDispatch(VkDevice device, void *object } #if defined(WIN32) +// Find the list of registry files (names VulkanDriverName/VulkanDriverNameWow) in hkr. +// +// This function looks for filename in given device handle, filename is then added to return list +// function return true if filename was appended to reg_data list +// If error occures result is updated with failure reason +bool loaderGetDeviceRegistryEntry(const struct loader_instance *inst, char **reg_data,PDWORD total_size, DEVINST devID, VkResult *result) +{ + HKEY hkrKey = INVALID_HANDLE_VALUE; + DWORD requiredSize, dataType; + char *pVkDriverPath = NULL; + bool found = false; + + if (NULL == total_size || NULL == reg_data) { + *result = VK_ERROR_INITIALIZATION_FAILED; + return false; + } + + CONFIGRET status = CM_Open_DevNode_Key(devID, KEY_QUERY_VALUE, 0, RegDisposition_OpenExisting, &hkrKey, CM_REGISTRY_SOFTWARE); + if (status != CR_SUCCESS) { + loader_log(inst, VK_DEBUG_REPORT_WARNING_BIT_EXT, 0, + "loaderGetDeviceRegistryEntry: Failed to open registry key for DeviceID(%d)", devID); + *result = VK_ERROR_INITIALIZATION_FAILED; + return false; + } + + // query value + LSTATUS ret = RegQueryValueEx( + hkrKey, + HKR_VK_DRIVER_NAME, + NULL, + NULL, + NULL, + &requiredSize); + + if (ret != ERROR_SUCCESS) { + loader_log(inst, VK_DEBUG_REPORT_WARNING_BIT_EXT, 0, + "loaderGetDeviceRegistryEntry: DeviceID(%d) Failed to obtain VulkanDriverName size", devID); + goto out; + } + + pVkDriverPath = loader_instance_heap_alloc(inst, requiredSize, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (pVkDriverPath == NULL) { + loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, + "loaderGetDeviceRegistryEntry: Failed to allocate space for DriverName."); + *result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + + ret = RegQueryValueEx( + hkrKey, + HKR_VK_DRIVER_NAME, + NULL, + &dataType, + pVkDriverPath, + &requiredSize + ); + + if (ret != ERROR_SUCCESS) { + loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, + "loaderGetDeviceRegistryEntry: DeviceID(%d) Failed to obtain VulkanDriverName"); + + *result = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + + if (dataType != REG_SZ) { + loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, + "loaderGetDeviceRegistryEntry: Invalid VulkanDriverName data type. Expected REG_SZ."); + *result = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + + if (NULL == *reg_data) { + *reg_data = loader_instance_heap_alloc(inst, *total_size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (NULL == *reg_data) { + loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, + "loaderGetDeviceRegistryEntry: Failed to allocate space for registry data for key %s", pVkDriverPath); + *result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + *reg_data[0] = '\0'; + } else if (strlen(*reg_data) + requiredSize + 1 > *total_size) { + void *new_ptr = loader_instance_heap_realloc(inst, *reg_data, *total_size, *total_size * 2, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (NULL == new_ptr) { + loader_log( + inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, + "loaderGetDeviceRegistryEntry: Failed to reallocate space for registry value of size %d for key %s", + *total_size * 2, pVkDriverPath); + *result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + *reg_data = new_ptr; + *total_size *= 2; + } + + if (strlen(*reg_data) == 0) { + (void)snprintf(*reg_data, requiredSize + 1, "%s", pVkDriverPath); + } else { + (void)snprintf(*reg_data + strlen(*reg_data), requiredSize + 2, "%c%s", PATH_SEPARATOR, pVkDriverPath); + } + found = true; + +out: + if (pVkDriverPath != NULL) { + loader_instance_heap_free(inst, pVkDriverPath); + } + RegCloseKey(hkrKey); + return found; +} + +// Find the list of registry files (names VulkanDriverName/VulkanDriverNameWow) in hkr . +// +// This function looks for display devices and childish software components +// for a list of files which are added to a returned list (function return +// value). +// Function return is a string with a ';' separated list of filenames. +// Function return is NULL if no valid name/value pairs are found in the key, +// or the key is not found. +// +// *reg_data contains a string list of filenames as pointer. +// When done using the returned string list, the caller should free the pointer. +VkResult loaderGetDeviceRegistryFiles(const struct loader_instance *inst, char **reg_data) { + static const char* softwareComponentGUID = "{5c4c3332-344d-483c-8739-259e934c9cc8}"; + static const char* displayGUID = "{4d36e968-e325-11ce-bfc1-08002be10318}"; + const ULONG flags = CM_GETIDLIST_FILTER_CLASS | CM_GETIDLIST_FILTER_PRESENT; + + char childGuid[MAX_GUID_STRING_LEN + 2]; // +2 for brackets {} + ULONG childGuidSize = sizeof(childGuid); + + DEVINST devID = 0, childID = 0; + DWORD totalSize = 4096; + char *pDeviceNames = NULL; + ULONG deviceNamesSize = 0; + VkResult result = VK_SUCCESS; + bool found = false; + + if (NULL == reg_data) { + result = VK_ERROR_INITIALIZATION_FAILED; + return result; + } + + // if after obtaining the DeviceNameSize, new device is added start over + do { + CM_Get_Device_ID_List_Size(&deviceNamesSize, displayGUID, flags); + + if (pDeviceNames != NULL) { + loader_instance_heap_free(inst, pDeviceNames); + } + + pDeviceNames = loader_instance_heap_alloc(inst, deviceNamesSize, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (pDeviceNames == NULL) { + loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, + "loaderGetDeviceRegistryFiles: Failed to allocate space for display device names."); + result = VK_ERROR_OUT_OF_HOST_MEMORY; + return false; + } + } while (CM_Get_Device_ID_List(displayGUID, pDeviceNames, deviceNamesSize, flags) == CR_BUFFER_SMALL); + + if (pDeviceNames) { + + for (char *deviceName = pDeviceNames; *deviceName; deviceName += strlen(deviceName) + 1) { + CONFIGRET status = CM_Locate_DevNode(&devID, deviceName, CM_LOCATE_DEVNODE_NORMAL); + if (CR_SUCCESS != status) { + loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, + "loaderGetRegistryFiles: failed to open DevNode %s", deviceName); + continue; + } + ULONG ulStatus, ulProblem; + status = CM_Get_DevNode_Status(&ulStatus, &ulProblem, devID, 0); + + if (CR_SUCCESS != status) + { + loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, + "loaderGetRegistryFiles: failed to probe device status %s", deviceName); + continue; + } + if ((ulStatus & DN_HAS_PROBLEM) && (ulProblem == CM_PROB_NEED_RESTART || ulProblem == DN_NEED_RESTART)) + { + loader_log(inst, VK_DEBUG_REPORT_WARNING_BIT_EXT, 0, + "loaderGetRegistryFiles: device %s is pending reboot, skipping ...", deviceName); + continue; + } + + loader_log(inst, VK_DEBUG_REPORT_WARNING_BIT_EXT, 0, + "loaderGetRegistryFiles: opening device %s", deviceName); + + if (loaderGetDeviceRegistryEntry(inst, reg_data, &totalSize, devID, &result)) { + found = true; + continue; + } + + status = CM_Get_Child(&childID, devID, 0); + if (status != CR_SUCCESS) { + loader_log(inst, VK_DEBUG_REPORT_WARNING_BIT_EXT, 0, + "loaderGetRegistryFiles: unable to open child-device error:%d", status); + continue; + } + + do { + char buffer[MAX_DEVICE_ID_LEN]; + CM_Get_Device_ID(childID, buffer, MAX_DEVICE_ID_LEN, 0); + + loader_log(inst, VK_DEBUG_REPORT_WARNING_BIT_EXT, 0, + "loaderGetRegistryFiles: Opening child device %d - %s", childID, buffer); + + status = CM_Get_DevNode_Registry_Property(childID, CM_DRP_CLASSGUID, NULL, &childGuid, &childGuidSize, 0); + if (status != CR_SUCCESS) { + loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, + "loaderGetRegistryFiles: unable to obtain GUID for:%d error:%d", childID, status); + + result = VK_ERROR_INITIALIZATION_FAILED; + continue; + } + + if (strcmp(childGuid, softwareComponentGUID) != 0) { + loader_log(inst, VK_DEBUG_REPORT_DEBUG_BIT_EXT, 0, + "loaderGetRegistryFiles: GUID for %d is not SoftwareComponent skipping", childID); + continue; + } + + if (loaderGetDeviceRegistryEntry(inst, reg_data, &totalSize, childID, &result)) { + found = true; + break; // check next-display-device + } + + } while (CM_Get_Sibling(&childID, childID, 0) == CR_SUCCESS); + } + + loader_instance_heap_free(inst, pDeviceNames); + } + + if (!found && result != VK_ERROR_OUT_OF_HOST_MEMORY) { + result = VK_ERROR_INITIALIZATION_FAILED; + } + + return result; +} + static char *loader_get_next_path(char *path); // Find the list of registry files (names within a key) in key "location". @@ -491,7 +736,7 @@ VkResult loaderGetRegistryFiles(const struct loader_instance *inst, char *locati } } - if (!found) { + if (!found && result != VK_ERROR_OUT_OF_HOST_MEMORY) { result = VK_ERROR_INITIALIZATION_FAILED; } @@ -2746,14 +2991,24 @@ static VkResult loader_get_manifest_files(const struct loader_instance *inst, co *loc_write = '\0'; #if defined(_WIN32) + VkResult regHKR_result = VK_SUCCESS; + + if (!strncmp(loc, DEFAULT_VK_DRIVERS_INFO, sizeof(DEFAULT_VK_DRIVERS_INFO))) + { + regHKR_result = loaderGetDeviceRegistryFiles(inst, ®); + } + VkResult reg_result = loaderGetRegistryFiles(inst, loc, is_layer, ®); - if (VK_SUCCESS != reg_result || NULL == reg) { + + if ((VK_SUCCESS != reg_result && VK_SUCCESS != regHKR_result) || NULL == reg) { if (!is_layer) { loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, "loader_get_manifest_files: Registry lookup failed " "to get ICD manifest files. Possibly missing Vulkan" " driver?"); - if (VK_SUCCESS == reg_result || VK_ERROR_OUT_OF_HOST_MEMORY == reg_result) { + if (VK_SUCCESS == regHKR_result || VK_ERROR_OUT_OF_HOST_MEMORY == regHKR_result) { + res = regHKR_result; + } else if (VK_SUCCESS == reg_result || VK_ERROR_OUT_OF_HOST_MEMORY == reg_result) { res = reg_result; } else { res = VK_ERROR_INCOMPATIBLE_DRIVER; diff --git a/loader/vk_loader_platform.h b/loader/vk_loader_platform.h index c1ae0d85..324de88e 100644 --- a/loader/vk_loader_platform.h +++ b/loader/vk_loader_platform.h @@ -172,6 +172,11 @@ static inline void loader_platform_thread_cond_broadcast(loader_platform_thread_ #define SECONDARY_VK_REGISTRY_HIVE HKEY_CURRENT_USER #define SECONDARY_VK_REGISTRY_HIVE_STR "HKEY_CURRENT_USER" #define DEFAULT_VK_DRIVERS_INFO "SOFTWARE\\Khronos\\" API_NAME "\\Drivers" +#ifdef _WIN64 +#define HKR_VK_DRIVER_NAME API_NAME "DriverName" +#else +#define HKR_VK_DRIVER_NAME API_NAME "DriverNameWow" +#endif #define DEFAULT_VK_DRIVERS_PATH "" #define DEFAULT_VK_ELAYERS_INFO "SOFTWARE\\Khronos\\" API_NAME "\\ExplicitLayers" #define DEFAULT_VK_ILAYERS_INFO "SOFTWARE\\Khronos\\" API_NAME "\\ImplicitLayers" -- 2.34.1