loader: Add new ICD search paths to loader
authorSlawomir Cygan <slawomir.cygan@intel.com>
Mon, 3 Jul 2017 14:47:52 +0000 (16:47 +0200)
committerLenny Komow <lenny@lunarg.com>
Thu, 10 Aug 2017 15:49:24 +0000 (09:49 -0600)
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
loader/LoaderAndLayerInterface.md
loader/loader.c
loader/vk_loader_platform.h

index 4a70e39f56965646e0725d1ac24df06e095eaca1..18ce4253f50e0e9c435f03a5f38d97217d0c6ea2 100644 (file)
@@ -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)
index cf3f4c6001f6881116fe7f5a20cb33921bbe85e5..f3d0944b9b2c8d6241b77f7da441176758c33391 100644 (file)
@@ -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.
 
index 468804f5b725841fdc45bec27e1b2f08e9b25ec7..c75bcf794cc5be92157ee057c6c1b743b8a05233 100644 (file)
 #include "cJSON.h"
 #include "murmurhash.h"
 
+#if defined(_WIN32)
+#include <Cfgmgr32.h>
+#include <initguid.h>
+#include <Devpkey.h>
+#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, &reg);
+        }
+
         VkResult reg_result = loaderGetRegistryFiles(inst, loc, is_layer, &reg);
-        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;
index c1ae0d85f8df15daea7b9a2b81e06670a4b9e6ac..324de88e08f9f8746d9ad29673405d9c9c715430 100644 (file)
@@ -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"