1 /*
2  *
3  * Copyright (c) 2014-2022 The Khronos Group Inc.
4  * Copyright (c) 2014-2022 Valve Corporation
5  * Copyright (c) 2014-2022 LunarG, Inc.
6  * Copyright (C) 2015 Google Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  * Author: Jon Ashburn <jon@lunarg.com>
21  * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
22  * Author: Chia-I Wu <olvaffe@gmail.com>
23  * Author: Chia-I Wu <olv@lunarg.com>
24  * Author: Mark Lobodzinski <mark@LunarG.com>
25  * Author: Lenny Komow <lenny@lunarg.com>
26  * Author: Charles Giessen <charles@lunarg.com>
27  *
28  */
29 // Windows only header file, guard it so that accidental inclusion doesn't cause unknown header include errors
30 #if defined(_WIN32)
31 
32 // This needs to be defined first, or else we'll get redefinitions on NTSTATUS values
33 #define UMDF_USING_NTSTATUS
34 #include <ntstatus.h>
35 
36 #include "loader_windows.h"
37 
38 #include "allocation.h"
39 #include "loader_environment.h"
40 #include "loader.h"
41 #include "log.h"
42 
43 #include <cfgmgr32.h>
44 #include <initguid.h>
45 #include <devpkey.h>
46 #include <winternl.h>
47 #include <strsafe.h>
48 #if defined(__MINGW32__)
49 #undef strcpy  // fix error with redefined strcpy when building with MinGW-w64
50 #endif
51 #include <dxgi1_6.h>
52 #include "adapters.h"
53 
54 #if !defined(__MINGW32__)
55 // not yet available with MinGW-w64 stable
56 #include <appmodel.h>
57 #endif
58 
59 #if !defined(NDEBUG)
60 #include <crtdbg.h>
61 #endif
62 
63 typedef HRESULT(APIENTRY *PFN_CreateDXGIFactory1)(REFIID riid, void **ppFactory);
64 PFN_CreateDXGIFactory1 fpCreateDXGIFactory1;
65 
66 // Empty function just so windows_initialization can find the current module location
function_for_finding_the_current_module(void)67 void function_for_finding_the_current_module(void) {}
68 
windows_initialization(void)69 void windows_initialization(void) {
70     char dll_location[MAX_PATH];
71     HMODULE module_handle = NULL;
72 
73     // Get a module handle to a static function inside of this source
74     if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
75                           (LPCSTR)&function_for_finding_the_current_module, &module_handle) != 0 &&
76         GetModuleFileName(module_handle, dll_location, sizeof(dll_location)) != 0) {
77         loader_log(NULL, VULKAN_LOADER_INFO_BIT, 0, "Using Vulkan Loader %s", dll_location);
78     }
79 
80     // This is needed to ensure that newer APIs are available right away
81     // and not after the first call that has been statically linked
82     LoadLibrary("gdi32.dll");
83 
84     wchar_t systemPath[MAX_PATH] = L"";
85     GetSystemDirectoryW(systemPath, MAX_PATH);
86     StringCchCatW(systemPath, MAX_PATH, L"\\dxgi.dll");
87     HMODULE dxgi_module = LoadLibraryW(systemPath);
88     fpCreateDXGIFactory1 =
89         dxgi_module == NULL ? NULL : (PFN_CreateDXGIFactory1)(void *)GetProcAddress(dxgi_module, "CreateDXGIFactory1");
90 
91 #if !defined(NDEBUG)
92     _set_error_mode(_OUT_TO_STDERR);
93     _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
94     _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
95 #endif
96 }
97 
DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved)98 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) {
99     (void)hinst;
100     switch (reason) {
101         case DLL_PROCESS_ATTACH:
102             loader_initialize();
103             break;
104         case DLL_PROCESS_DETACH:
105             if (NULL == reserved) {
106                 loader_release();
107             }
108             break;
109         default:
110             // Do nothing
111             break;
112     }
113     return TRUE;
114 }
115 
windows_add_json_entry(const struct loader_instance *inst, char **reg_data, PDWORD total_size, LPCSTR key_name, DWORD key_type, LPSTR json_path, DWORD json_size, VkResult *result)116 bool windows_add_json_entry(const struct loader_instance *inst,
117                             char **reg_data,    // list of JSON files
118                             PDWORD total_size,  // size of reg_data
119                             LPCSTR key_name,    // key name - used for debug prints - i.e. VulkanDriverName
120                             DWORD key_type,     // key data type
121                             LPSTR json_path,    // JSON string to add to the list reg_data
122                             DWORD json_size,    // size in bytes of json_path
123                             VkResult *result) {
124     // Check for and ignore duplicates.
125     if (*reg_data && strstr(*reg_data, json_path)) {
126         // Success. The json_path is already in the list.
127         return true;
128     }
129 
130     if (NULL == *reg_data) {
131         *reg_data = loader_instance_heap_alloc(inst, *total_size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
132         if (NULL == *reg_data) {
133             loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
134                        "windows_add_json_entry: Failed to allocate space for registry data for key %s", json_path);
135             *result = VK_ERROR_OUT_OF_HOST_MEMORY;
136             return false;
137         }
138         *reg_data[0] = '\0';
139     } else if (strlen(*reg_data) + json_size + 1 > *total_size) {
140         void *new_ptr =
141             loader_instance_heap_realloc(inst, *reg_data, *total_size, *total_size * 2, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
142         if (NULL == new_ptr) {
143             loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
144                        "windows_add_json_entry: Failed to reallocate space for registry value of size %d for key %s",
145                        *total_size * 2, json_path);
146             *result = VK_ERROR_OUT_OF_HOST_MEMORY;
147             return false;
148         }
149         *reg_data = new_ptr;
150         *total_size *= 2;
151     }
152 
153     for (char *curr_filename = json_path; curr_filename[0] != '\0'; curr_filename += strlen(curr_filename) + 1) {
154         if (strlen(*reg_data) == 0) {
155             (void)snprintf(*reg_data, json_size + 1, "%s", curr_filename);
156         } else {
157             (void)snprintf(*reg_data + strlen(*reg_data), json_size + 2, "%c%s", PATH_SEPARATOR, curr_filename);
158         }
159         loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "%s: Located json file \"%s\" from PnP registry: %s", __FUNCTION__,
160                    curr_filename, key_name);
161 
162         if (key_type == REG_SZ) {
163             break;
164         }
165     }
166     return true;
167 }
168 
windows_get_device_registry_entry(const struct loader_instance *inst, char **reg_data, PDWORD total_size, DEVINST dev_id, LPCSTR value_name, VkResult *result)169 bool windows_get_device_registry_entry(const struct loader_instance *inst, char **reg_data, PDWORD total_size, DEVINST dev_id,
170                                        LPCSTR value_name, VkResult *result) {
171     HKEY hkrKey = INVALID_HANDLE_VALUE;
172     DWORD requiredSize, data_type;
173     char *manifest_path = NULL;
174     bool found = false;
175 
176     assert(reg_data != NULL && "windows_get_device_registry_entry: reg_data is a NULL pointer");
177     assert(total_size != NULL && "windows_get_device_registry_entry: total_size is a NULL pointer");
178 
179     CONFIGRET status = CM_Open_DevNode_Key(dev_id, KEY_QUERY_VALUE, 0, RegDisposition_OpenExisting, &hkrKey, CM_REGISTRY_SOFTWARE);
180     if (status != CR_SUCCESS) {
181         loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
182                    "windows_get_device_registry_entry: Failed to open registry key for DeviceID(%d)", dev_id);
183         *result = VK_ERROR_INCOMPATIBLE_DRIVER;
184         return false;
185     }
186 
187     // query value
188     LSTATUS ret = RegQueryValueEx(hkrKey, value_name, NULL, NULL, NULL, &requiredSize);
189 
190     if (ret != ERROR_SUCCESS) {
191         if (ret == ERROR_FILE_NOT_FOUND) {
192             loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
193                        "windows_get_device_registry_entry: Device ID(%d) Does not contain a value for \"%s\"", dev_id, value_name);
194         } else {
195             loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
196                        "windows_get_device_registry_entry: DeviceID(%d) Failed to obtain %s size", dev_id, value_name);
197         }
198         goto out;
199     }
200 
201     manifest_path = loader_instance_heap_alloc(inst, requiredSize, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
202     if (manifest_path == NULL) {
203         loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
204                    "windows_get_device_registry_entry: Failed to allocate space for DriverName.");
205         *result = VK_ERROR_OUT_OF_HOST_MEMORY;
206         goto out;
207     }
208 
209     ret = RegQueryValueEx(hkrKey, value_name, NULL, &data_type, (BYTE *)manifest_path, &requiredSize);
210 
211     if (ret != ERROR_SUCCESS) {
212         loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
213                    "windows_get_device_registry_entry: DeviceID(%d) Failed to obtain %s", value_name);
214         *result = VK_ERROR_INCOMPATIBLE_DRIVER;
215         goto out;
216     }
217 
218     if (data_type != REG_SZ && data_type != REG_MULTI_SZ) {
219         loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
220                    "windows_get_device_registry_entry: Invalid %s data type. Expected REG_SZ or REG_MULTI_SZ.", value_name);
221         *result = VK_ERROR_INCOMPATIBLE_DRIVER;
222         goto out;
223     }
224 
225     found = windows_add_json_entry(inst, reg_data, total_size, value_name, data_type, manifest_path, requiredSize, result);
226 
227 out:
228     loader_instance_heap_free(inst, manifest_path);
229     RegCloseKey(hkrKey);
230     return found;
231 }
232 
windows_get_device_registry_files(const struct loader_instance *inst, uint32_t log_target_flag, char **reg_data, PDWORD reg_data_size, LPCSTR value_name)233 VkResult windows_get_device_registry_files(const struct loader_instance *inst, uint32_t log_target_flag, char **reg_data,
234                                            PDWORD reg_data_size, LPCSTR value_name) {
235     const wchar_t *softwareComponentGUID = L"{5c4c3332-344d-483c-8739-259e934c9cc8}";
236     const wchar_t *displayGUID = L"{4d36e968-e325-11ce-bfc1-08002be10318}";
237 #if defined(CM_GETIDLIST_FILTER_PRESENT)
238     const ULONG flags = CM_GETIDLIST_FILTER_CLASS | CM_GETIDLIST_FILTER_PRESENT;
239 #else
240     const ULONG flags = 0x300;
241 #endif
242 
243     wchar_t childGuid[MAX_GUID_STRING_LEN + 2];  // +2 for brackets {}
244     for (uint32_t i = 0; i < MAX_GUID_STRING_LEN + 2; i++) {
245         childGuid[i] = L'\0';
246     }
247     ULONG childGuidSize = sizeof(childGuid);
248 
249     DEVINST devID = 0, childID = 0;
250     wchar_t *pDeviceNames = NULL;
251     ULONG deviceNamesSize = 0;
252     VkResult result = VK_SUCCESS;
253     bool found = false;
254 
255     assert(reg_data != NULL && "windows_get_device_registry_files: reg_data is NULL");
256 
257     // if after obtaining the DeviceNameSize, new device is added start over
258     do {
259         CM_Get_Device_ID_List_SizeW(&deviceNamesSize, displayGUID, flags);
260 
261         loader_instance_heap_free(inst, pDeviceNames);
262 
263         pDeviceNames = loader_instance_heap_alloc(inst, deviceNamesSize * sizeof(wchar_t), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
264         if (pDeviceNames == NULL) {
265             loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
266                        "windows_get_device_registry_files: Failed to allocate space for display device names.");
267             result = VK_ERROR_OUT_OF_HOST_MEMORY;
268             return result;
269         }
270     } while (CM_Get_Device_ID_ListW(displayGUID, pDeviceNames, deviceNamesSize, flags) == CR_BUFFER_SMALL);
271 
272     if (pDeviceNames) {
273         for (wchar_t *deviceName = pDeviceNames; *deviceName; deviceName += wcslen(deviceName) + 1) {
274             CONFIGRET status = CM_Locate_DevNodeW(&devID, deviceName, CM_LOCATE_DEVNODE_NORMAL);
275             if (CR_SUCCESS != status) {
276                 loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
277                            "windows_get_device_registry_files: failed to open DevNode %ls", deviceName);
278                 continue;
279             }
280             ULONG ulStatus, ulProblem;
281             status = CM_Get_DevNode_Status(&ulStatus, &ulProblem, devID, 0);
282 
283             if (CR_SUCCESS != status) {
284                 loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
285                            "windows_get_device_registry_files: failed to probe device status %ls", deviceName);
286                 continue;
287             }
288             if ((ulStatus & DN_HAS_PROBLEM) && (ulProblem == CM_PROB_NEED_RESTART || ulProblem == DN_NEED_RESTART)) {
289                 loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
290                            "windows_get_device_registry_files: device %ls is pending reboot, skipping ...", deviceName);
291                 continue;
292             }
293 
294             loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0, "windows_get_device_registry_files: opening device %ls",
295                        deviceName);
296 
297             if (windows_get_device_registry_entry(inst, reg_data, reg_data_size, devID, value_name, &result)) {
298                 found = true;
299                 continue;
300             } else if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
301                 break;
302             }
303 
304             status = CM_Get_Child(&childID, devID, 0);
305             if (status != CR_SUCCESS) {
306                 loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
307                            "windows_get_device_registry_files: unable to open child-device error:%d", status);
308                 continue;
309             }
310 
311             do {
312                 wchar_t buffer[MAX_DEVICE_ID_LEN];
313                 CM_Get_Device_IDW(childID, buffer, MAX_DEVICE_ID_LEN, 0);
314 
315                 loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
316                            "windows_get_device_registry_files: Opening child device %d - %ls", childID, buffer);
317 
318                 status = CM_Get_DevNode_Registry_PropertyW(childID, CM_DRP_CLASSGUID, NULL, &childGuid, &childGuidSize, 0);
319                 if (status != CR_SUCCESS) {
320                     loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
321                                "windows_get_device_registry_files: unable to obtain GUID for:%d error:%d", childID, status);
322 
323                     result = VK_ERROR_INCOMPATIBLE_DRIVER;
324                     continue;
325                 }
326 
327                 if (wcscmp(childGuid, softwareComponentGUID) != 0) {
328                     loader_log(inst, VULKAN_LOADER_DEBUG_BIT | log_target_flag, 0,
329                                "windows_get_device_registry_files: GUID for %d is not SoftwareComponent skipping", childID);
330                     continue;
331                 }
332 
333                 if (windows_get_device_registry_entry(inst, reg_data, reg_data_size, childID, value_name, &result)) {
334                     found = true;
335                     break;  // check next-display-device
336                 }
337 
338             } while (CM_Get_Sibling(&childID, childID, 0) == CR_SUCCESS);
339         }
340 
341         loader_instance_heap_free(inst, pDeviceNames);
342     }
343 
344     if (!found && result != VK_ERROR_OUT_OF_HOST_MEMORY) {
345         loader_log(inst, log_target_flag, 0, "windows_get_device_registry_files: found no registry files");
346         result = VK_ERROR_INCOMPATIBLE_DRIVER;
347     }
348 
349     return result;
350 }
351 
windows_get_registry_files(const struct loader_instance *inst, char *location, bool use_secondary_hive, char **reg_data, PDWORD reg_data_size)352 VkResult windows_get_registry_files(const struct loader_instance *inst, char *location, bool use_secondary_hive, char **reg_data,
353                                     PDWORD reg_data_size) {
354     // This list contains all of the allowed ICDs. This allows us to verify that a device is actually present from the vendor
355     // specified. This does disallow other vendors, but any new driver should use the device-specific registries anyway.
356     const struct {
357         const char *filename;
358         unsigned int vendor_id;
359     } known_drivers[] = {
360 #if defined(_WIN64)
361         {
362             .filename = "igvk64.json",
363             .vendor_id = 0x8086,
364         },
365         {
366             .filename = "nv-vk64.json",
367             .vendor_id = 0x10de,
368         },
369         {
370             .filename = "amd-vulkan64.json",
371             .vendor_id = 0x1002,
372         },
373         {
374             .filename = "amdvlk64.json",
375             .vendor_id = 0x1002,
376         },
377 #else
378         {
379             .filename = "igvk32.json",
380             .vendor_id = 0x8086,
381         },
382         {
383             .filename = "nv-vk32.json",
384             .vendor_id = 0x10de,
385         },
386         {
387             .filename = "amd-vulkan32.json",
388             .vendor_id = 0x1002,
389         },
390         {
391             .filename = "amdvlk32.json",
392             .vendor_id = 0x1002,
393         },
394 #endif
395     };
396 
397     LONG rtn_value;
398     HKEY hive = DEFAULT_VK_REGISTRY_HIVE, key;
399     DWORD access_flags;
400     char name[2048];
401     char *loc = location;
402     char *next;
403     DWORD name_size = sizeof(name);
404     DWORD value;
405     DWORD value_size = sizeof(value);
406     VkResult result = VK_SUCCESS;
407     bool found = false;
408     IDXGIFactory1 *dxgi_factory = NULL;
409     bool is_driver = !strcmp(location, VK_DRIVERS_INFO_REGISTRY_LOC);
410     uint32_t log_target_flag = is_driver ? VULKAN_LOADER_DRIVER_BIT : VULKAN_LOADER_LAYER_BIT;
411 
412     assert(reg_data != NULL && "windows_get_registry_files: reg_data is a NULL pointer");
413 
414     if (is_driver) {
415         HRESULT hres = fpCreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&dxgi_factory);
416         if (hres != S_OK) {
417             loader_log(inst, VULKAN_LOADER_WARN_BIT | log_target_flag, 0,
418                        "windows_get_registry_files: Failed to create dxgi factory for ICD registry verification. No ICDs will be "
419                        "added from "
420                        "legacy registry locations");
421             goto out;
422         }
423     }
424 
425     while (*loc) {
426         next = loader_get_next_path(loc);
427         access_flags = KEY_QUERY_VALUE;
428         rtn_value = RegOpenKeyEx(hive, loc, 0, access_flags, &key);
429         if (ERROR_SUCCESS == rtn_value) {
430             for (DWORD idx = 0;
431                  (rtn_value = RegEnumValue(key, idx++, name, &name_size, NULL, NULL, (LPBYTE)&value, &value_size)) == ERROR_SUCCESS;
432                  name_size = sizeof(name), value_size = sizeof(value)) {
433                 if (value_size == sizeof(value) && value == 0) {
434                     if (NULL == *reg_data) {
435                         *reg_data = loader_instance_heap_alloc(inst, *reg_data_size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
436                         if (NULL == *reg_data) {
437                             loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
438                                        "windows_get_registry_files: Failed to allocate space for registry data for key %s", name);
439                             RegCloseKey(key);
440                             result = VK_ERROR_OUT_OF_HOST_MEMORY;
441                             goto out;
442                         }
443                         *reg_data[0] = '\0';
444                     } else if (strlen(*reg_data) + name_size + 1 > *reg_data_size) {
445                         void *new_ptr = loader_instance_heap_realloc(inst, *reg_data, *reg_data_size, *reg_data_size * 2,
446                                                                      VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
447                         if (NULL == new_ptr) {
448                             loader_log(
449                                 inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
450                                 "windows_get_registry_files: Failed to reallocate space for registry value of size %d for key %s",
451                                 *reg_data_size * 2, name);
452                             RegCloseKey(key);
453                             result = VK_ERROR_OUT_OF_HOST_MEMORY;
454                             goto out;
455                         }
456                         *reg_data = new_ptr;
457                         *reg_data_size *= 2;
458                     }
459 
460                     // We've now found a json file. If this is an ICD, we still need to check if there is actually a device
461                     // that matches this ICD
462                     loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
463                                "Located json file \"%s\" from registry \"%s\\%s\"", name,
464                                hive == DEFAULT_VK_REGISTRY_HIVE ? DEFAULT_VK_REGISTRY_HIVE_STR : SECONDARY_VK_REGISTRY_HIVE_STR,
465                                location);
466                     if (is_driver) {
467                         uint32_t i = 0;
468                         for (i = 0; i < sizeof(known_drivers) / sizeof(known_drivers[0]); ++i) {
469                             if (!strcmp(name + strlen(name) - strlen(known_drivers[i].filename), known_drivers[i].filename)) {
470                                 break;
471                             }
472                         }
473                         if (i == sizeof(known_drivers) / sizeof(known_drivers[0])) {
474                             loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
475                                        "Driver %s is not recognized as a known driver. It will be assumed to be active", name);
476                         } else {
477                             bool found_gpu = false;
478                             for (int j = 0;; ++j) {
479                                 IDXGIAdapter1 *adapter;
480                                 HRESULT hres = dxgi_factory->lpVtbl->EnumAdapters1(dxgi_factory, j, &adapter);
481                                 if (hres == DXGI_ERROR_NOT_FOUND) {
482                                     break;
483                                 } else if (hres != S_OK) {
484                                     loader_log(inst, VULKAN_LOADER_WARN_BIT | log_target_flag, 0,
485                                                "Failed to enumerate DXGI adapters at index %d. As a result, drivers may be skipped",
486                                                j);
487                                     continue;
488                                 }
489 
490                                 DXGI_ADAPTER_DESC1 description;
491                                 hres = adapter->lpVtbl->GetDesc1(adapter, &description);
492                                 if (hres != S_OK) {
493                                     loader_log(
494                                         inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
495                                         "Failed to get DXGI adapter information at index %d. As a result, drivers may be skipped",
496                                         j);
497                                     continue;
498                                 }
499 
500                                 if (description.VendorId == known_drivers[i].vendor_id) {
501                                     found_gpu = true;
502                                     break;
503                                 }
504                             }
505 
506                             if (!found_gpu) {
507                                 loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
508                                            "Dropping driver %s as no corresponding DXGI adapter was found", name);
509                                 continue;
510                             }
511                         }
512                     }
513 
514                     if (strlen(*reg_data) == 0) {
515                         // The list is emtpy. Add the first entry.
516                         (void)snprintf(*reg_data, name_size + 1, "%s", name);
517                         found = true;
518                     } else {
519                         // At this point the reg_data variable contains other JSON paths, likely from the PNP/device section
520                         // of the registry that we want to have precedence over this non-device specific section of the registry.
521                         // To make sure we avoid enumerating old JSON files/drivers that might be present in the non-device specific
522                         // area of the registry when a newer device specific JSON file is present, do a check before adding.
523                         // Find the file name, without path, of the JSON file found in the non-device specific registry location.
524                         // If the same JSON file name is already found in the list, don't add it again.
525                         bool foundDuplicate = false;
526                         char *pLastSlashName = strrchr(name, '\\');
527                         if (pLastSlashName != NULL) {
528                             char *foundMatch = strstr(*reg_data, pLastSlashName + 1);
529                             if (foundMatch != NULL) {
530                                 foundDuplicate = true;
531                             }
532                         }
533                         // Only skip if we are adding a driver and a duplicate was found
534                         if (!is_driver || (is_driver && foundDuplicate == false)) {
535                             // Add the new entry to the list.
536                             (void)snprintf(*reg_data + strlen(*reg_data), name_size + 2, "%c%s", PATH_SEPARATOR, name);
537                             found = true;
538                         } else {
539                             loader_log(
540                                 inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
541                                 "Skipping adding of json file \"%s\" from registry \"%s\\%s\" to the list due to duplication", name,
542                                 hive == DEFAULT_VK_REGISTRY_HIVE ? DEFAULT_VK_REGISTRY_HIVE_STR : SECONDARY_VK_REGISTRY_HIVE_STR,
543                                 location);
544                         }
545                     }
546                 }
547             }
548             RegCloseKey(key);
549         }
550 
551         // Advance the location - if the next location is in the secondary hive, then reset the locations and advance the hive
552         if (use_secondary_hive && (hive == DEFAULT_VK_REGISTRY_HIVE) && (*next == '\0')) {
553             loc = location;
554             hive = SECONDARY_VK_REGISTRY_HIVE;
555         } else {
556             loc = next;
557         }
558     }
559 
560     if (!found && result != VK_ERROR_OUT_OF_HOST_MEMORY) {
561         loader_log(inst, log_target_flag, 0, "Found no registry files in %s\\%s",
562                    (hive == DEFAULT_VK_REGISTRY_HIVE) ? DEFAULT_VK_REGISTRY_HIVE_STR : SECONDARY_VK_REGISTRY_HIVE_STR, location);
563         result = VK_ERROR_INCOMPATIBLE_DRIVER;
564     }
565 
566 out:
567     if (is_driver && dxgi_factory != NULL) {
568         dxgi_factory->lpVtbl->Release(dxgi_factory);
569     }
570 
571     return result;
572 }
573 
574 // Read manifest JSON files using the Windows driver interface
windows_read_manifest_from_d3d_adapters(const struct loader_instance *inst, char **reg_data, PDWORD reg_data_size, const wchar_t *value_name)575 VkResult windows_read_manifest_from_d3d_adapters(const struct loader_instance *inst, char **reg_data, PDWORD reg_data_size,
576                                                  const wchar_t *value_name) {
577     VkResult result = VK_INCOMPLETE;
578     LoaderEnumAdapters2 adapters = {.adapter_count = 0, .adapters = NULL};
579     LoaderQueryRegistryInfo *full_info = NULL;
580     size_t full_info_size = 0;
581     char *json_path = NULL;
582     size_t json_path_size = 0;
583 
584     HMODULE gdi32_dll = GetModuleHandle("gdi32.dll");
585     if (gdi32_dll == NULL) {
586         result = VK_ERROR_INCOMPATIBLE_DRIVER;
587         goto out;
588     }
589 
590     PFN_LoaderEnumAdapters2 fpLoaderEnumAdapters2 =
591         (PFN_LoaderEnumAdapters2)(void *)GetProcAddress(gdi32_dll, "D3DKMTEnumAdapters2");
592     PFN_LoaderQueryAdapterInfo fpLoaderQueryAdapterInfo =
593         (PFN_LoaderQueryAdapterInfo)(void *)GetProcAddress(gdi32_dll, "D3DKMTQueryAdapterInfo");
594     if (fpLoaderEnumAdapters2 == NULL || fpLoaderQueryAdapterInfo == NULL) {
595         result = VK_ERROR_INCOMPATIBLE_DRIVER;
596         goto out;
597     }
598 
599     // Get all of the adapters
600     NTSTATUS status = fpLoaderEnumAdapters2(&adapters);
601     if (status == STATUS_SUCCESS && adapters.adapter_count > 0) {
602         adapters.adapters = loader_instance_heap_alloc(inst, sizeof(*adapters.adapters) * adapters.adapter_count,
603                                                        VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
604         if (adapters.adapters == NULL) {
605             goto out;
606         }
607         status = fpLoaderEnumAdapters2(&adapters);
608     }
609     if (status != STATUS_SUCCESS) {
610         goto out;
611     }
612 
613     // If that worked, we need to get the manifest file(s) for each adapter
614     for (ULONG i = 0; i < adapters.adapter_count; ++i) {
615         // The first query should just check if the field exists and how big it is
616         LoaderQueryRegistryInfo filename_info = {
617             .query_type = LOADER_QUERY_REGISTRY_ADAPTER_KEY,
618             .query_flags =
619                 {
620                     .translate_path = true,
621                 },
622             .value_type = REG_MULTI_SZ,
623             .physical_adapter_index = 0,
624         };
625         size_t value_name_size = wcslen(value_name);
626         wcsncpy_s(filename_info.value_name, MAX_PATH, value_name, value_name_size);
627         LoaderQueryAdapterInfo query_info;
628         query_info.handle = adapters.adapters[i].handle;
629         query_info.type = LOADER_QUERY_TYPE_REGISTRY;
630         query_info.private_data = &filename_info;
631         query_info.private_data_size = sizeof(filename_info);
632         status = fpLoaderQueryAdapterInfo(&query_info);
633 
634         // This error indicates that the type didn't match, so we'll try a REG_SZ
635         if (status != STATUS_SUCCESS) {
636             filename_info.value_type = REG_SZ;
637             status = fpLoaderQueryAdapterInfo(&query_info);
638         }
639 
640         if (status != STATUS_SUCCESS || filename_info.status != LOADER_QUERY_REGISTRY_STATUS_BUFFER_OVERFLOW) {
641             continue;
642         }
643 
644         while (status == STATUS_SUCCESS &&
645                ((LoaderQueryRegistryInfo *)query_info.private_data)->status == LOADER_QUERY_REGISTRY_STATUS_BUFFER_OVERFLOW) {
646             bool needs_copy = (full_info == NULL);
647             size_t full_size = sizeof(LoaderQueryRegistryInfo) + filename_info.output_value_size;
648             void *buffer =
649                 loader_instance_heap_realloc(inst, full_info, full_info_size, full_size, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
650             if (buffer == NULL) {
651                 result = VK_ERROR_OUT_OF_HOST_MEMORY;
652                 goto out;
653             }
654             full_info = buffer;
655             full_info_size = full_size;
656 
657             if (needs_copy) {
658                 memcpy(full_info, &filename_info, sizeof(LoaderQueryRegistryInfo));
659             }
660             query_info.private_data = full_info;
661             query_info.private_data_size = (UINT)full_info_size;
662             status = fpLoaderQueryAdapterInfo(&query_info);
663         }
664 
665         if (status != STATUS_SUCCESS || full_info->status != LOADER_QUERY_REGISTRY_STATUS_SUCCESS) {
666             goto out;
667         }
668 
669         // Convert the wide string to a narrow string
670         void *buffer = loader_instance_heap_realloc(inst, json_path, json_path_size, full_info->output_value_size,
671                                                     VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
672         if (buffer == NULL) {
673             result = VK_ERROR_OUT_OF_HOST_MEMORY;
674             goto out;
675         }
676         json_path = buffer;
677         json_path_size = full_info->output_value_size;
678 
679         // Iterate over each component string
680         for (const wchar_t *curr_path = full_info->output_string; curr_path[0] != '\0'; curr_path += wcslen(curr_path) + 1) {
681             WideCharToMultiByte(CP_UTF8, 0, curr_path, -1, json_path, (int)json_path_size, NULL, NULL);
682 
683             // Add the string to the output list
684             result = VK_SUCCESS;
685             windows_add_json_entry(inst, reg_data, reg_data_size, (LPCTSTR)L"EnumAdapters", REG_SZ, json_path,
686                                    (DWORD)strlen(json_path) + 1, &result);
687             if (result != VK_SUCCESS) {
688                 goto out;
689             }
690 
691             // If this is a string and not a multi-string, we don't want to go through the loop more than once
692             if (full_info->value_type == REG_SZ) {
693                 break;
694             }
695         }
696     }
697 
698 out:
699     loader_instance_heap_free(inst, json_path);
700     loader_instance_heap_free(inst, full_info);
701     loader_instance_heap_free(inst, adapters.adapters);
702 
703     return result;
704 }
705 
706 // Look for data files in the registry.
windows_read_data_files_in_registry(const struct loader_instance *inst, enum loader_data_files_type data_file_type, bool warn_if_not_present, char *registry_location, struct loader_string_list *out_files)707 VkResult windows_read_data_files_in_registry(const struct loader_instance *inst, enum loader_data_files_type data_file_type,
708                                              bool warn_if_not_present, char *registry_location,
709                                              struct loader_string_list *out_files) {
710     VkResult vk_result = VK_SUCCESS;
711     char *search_path = NULL;
712     uint32_t log_target_flag = 0;
713 
714     if (data_file_type == LOADER_DATA_FILE_MANIFEST_DRIVER) {
715         log_target_flag = VULKAN_LOADER_DRIVER_BIT;
716         loader_log(inst, log_target_flag, 0, "Checking for Driver Manifest files in Registry at %s\\%s",
717                    DEFAULT_VK_REGISTRY_HIVE_STR, registry_location);
718     } else {
719         log_target_flag = VULKAN_LOADER_LAYER_BIT;
720         loader_log(inst, log_target_flag, 0, "Checking for Layer Manifest files in Registry at %s\\%s",
721                    DEFAULT_VK_REGISTRY_HIVE_STR, registry_location);
722     }
723 
724     // These calls look at the PNP/Device section of the registry.
725     VkResult regHKR_result = VK_SUCCESS;
726     DWORD reg_size = 4096;
727     if (!strncmp(registry_location, VK_DRIVERS_INFO_REGISTRY_LOC, sizeof(VK_DRIVERS_INFO_REGISTRY_LOC))) {
728         // If we're looking for drivers we need to try enumerating adapters
729         regHKR_result = windows_read_manifest_from_d3d_adapters(inst, &search_path, &reg_size, LoaderPnpDriverRegistryWide());
730         if (regHKR_result == VK_INCOMPLETE) {
731             regHKR_result =
732                 windows_get_device_registry_files(inst, log_target_flag, &search_path, &reg_size, LoaderPnpDriverRegistry());
733         }
734     } else if (!strncmp(registry_location, VK_ELAYERS_INFO_REGISTRY_LOC, sizeof(VK_ELAYERS_INFO_REGISTRY_LOC))) {
735         regHKR_result = windows_read_manifest_from_d3d_adapters(inst, &search_path, &reg_size, LoaderPnpELayerRegistryWide());
736         if (regHKR_result == VK_INCOMPLETE) {
737             regHKR_result =
738                 windows_get_device_registry_files(inst, log_target_flag, &search_path, &reg_size, LoaderPnpELayerRegistry());
739         }
740     } else if (!strncmp(registry_location, VK_ILAYERS_INFO_REGISTRY_LOC, sizeof(VK_ILAYERS_INFO_REGISTRY_LOC))) {
741         regHKR_result = windows_read_manifest_from_d3d_adapters(inst, &search_path, &reg_size, LoaderPnpILayerRegistryWide());
742         if (regHKR_result == VK_INCOMPLETE) {
743             regHKR_result =
744                 windows_get_device_registry_files(inst, log_target_flag, &search_path, &reg_size, LoaderPnpILayerRegistry());
745         }
746     }
747 
748     if (regHKR_result == VK_ERROR_OUT_OF_HOST_MEMORY) {
749         vk_result = VK_ERROR_OUT_OF_HOST_MEMORY;
750         goto out;
751     }
752 
753     // This call looks into the Khronos non-device specific section of the registry for layer files.
754     bool use_secondary_hive = (data_file_type != LOADER_DATA_FILE_MANIFEST_DRIVER) && (!is_high_integrity());
755     VkResult reg_result = windows_get_registry_files(inst, registry_location, use_secondary_hive, &search_path, &reg_size);
756     if (reg_result == VK_ERROR_OUT_OF_HOST_MEMORY) {
757         vk_result = VK_ERROR_OUT_OF_HOST_MEMORY;
758         goto out;
759     }
760 
761     if ((VK_SUCCESS != reg_result && VK_SUCCESS != regHKR_result) || NULL == search_path) {
762         if (data_file_type == LOADER_DATA_FILE_MANIFEST_DRIVER) {
763             loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
764                        "windows_read_data_files_in_registry: Registry lookup failed to get ICD manifest files.  Possibly missing "
765                        "Vulkan driver?");
766             vk_result = VK_ERROR_INCOMPATIBLE_DRIVER;
767         } else {
768             if (warn_if_not_present) {
769                 if (data_file_type == LOADER_DATA_FILE_MANIFEST_IMPLICIT_LAYER ||
770                     data_file_type == LOADER_DATA_FILE_MANIFEST_EXPLICIT_LAYER) {
771                     // This is only a warning for layers
772                     loader_log(inst, VULKAN_LOADER_WARN_BIT | log_target_flag, 0,
773                                "windows_read_data_files_in_registry: Registry lookup failed to get layer manifest files.");
774                 } else {
775                     // This is only a warning for general data files
776                     loader_log(inst, VULKAN_LOADER_WARN_BIT | log_target_flag, 0,
777                                "windows_read_data_files_in_registry: Registry lookup failed to get data files.");
778                 }
779             }
780             // Return success for now since it's not critical for layers
781             vk_result = VK_SUCCESS;
782         }
783         goto out;
784     }
785 
786     // Now, parse the paths and add any manifest files found in them.
787     vk_result = add_data_files(inst, search_path, out_files, false);
788 
789 out:
790 
791     loader_instance_heap_free(inst, search_path);
792 
793     return vk_result;
794 }
795 
enumerate_adapter_physical_devices(struct loader_instance *inst, struct loader_icd_term *icd_term, uint32_t icd_idx, LUID luid, uint32_t *icd_phys_devs_array_count, struct loader_icd_physical_devices *icd_phys_devs_array)796 VkResult enumerate_adapter_physical_devices(struct loader_instance *inst, struct loader_icd_term *icd_term, uint32_t icd_idx,
797                                             LUID luid, uint32_t *icd_phys_devs_array_count,
798                                             struct loader_icd_physical_devices *icd_phys_devs_array) {
799     uint32_t count = 0;
800     VkResult res = icd_term->scanned_icd->EnumerateAdapterPhysicalDevices(icd_term->instance, luid, &count, NULL);
801     if (res == VK_ERROR_OUT_OF_HOST_MEMORY) {
802         return res;
803     } else if (res == VK_ERROR_INCOMPATIBLE_DRIVER) {
804         return VK_SUCCESS;  // This driver doesn't support the adapter
805     } else if (res != VK_SUCCESS) {
806         loader_log(inst, VULKAN_LOADER_WARN_BIT, 0,
807                    "Failed to convert DXGI adapter into Vulkan physical device with unexpected error code");
808         return res;
809     } else if (0 == count) {
810         return VK_SUCCESS;  // This driver doesn't support the adapter
811     }
812 
813     // Take a pointer to the last element of icd_phys_devs_array to simplify usage
814     struct loader_icd_physical_devices *next_icd_phys_devs = &icd_phys_devs_array[*icd_phys_devs_array_count];
815 
816     // Get the actual physical devices
817     do {
818         next_icd_phys_devs->physical_devices = loader_instance_heap_realloc(
819             inst, next_icd_phys_devs->physical_devices, next_icd_phys_devs->device_count * sizeof(VkPhysicalDevice),
820             count * sizeof(VkPhysicalDevice), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
821         if (next_icd_phys_devs->physical_devices == NULL) {
822             return VK_ERROR_OUT_OF_HOST_MEMORY;
823         }
824         next_icd_phys_devs->device_count = count;
825     } while ((res = icd_term->scanned_icd->EnumerateAdapterPhysicalDevices(icd_term->instance, luid, &count,
826                                                                            next_icd_phys_devs->physical_devices)) == VK_INCOMPLETE);
827 
828     if (res != VK_SUCCESS) {
829         loader_instance_heap_free(inst, next_icd_phys_devs->physical_devices);
830         next_icd_phys_devs->physical_devices = NULL;
831         // Unless OOHM occurs, only return VK_SUCCESS
832         if (res != VK_ERROR_OUT_OF_HOST_MEMORY) {
833             res = VK_SUCCESS;
834             loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Failed to convert DXGI adapter into Vulkan physical device");
835         }
836         return res;
837     }
838 
839     // Because the loader calls EnumerateAdapterPhysicalDevices on all drivers with each DXGI Adapter, if there are multiple drivers
840     // that share a luid the physical device will get queried multiple times. We can prevent that by not adding them if the
841     // enumerated physical devices have already been added.
842     bool already_enumerated = false;
843     for (uint32_t j = 0; j < *icd_phys_devs_array_count; j++) {
844         if (count == icd_phys_devs_array[j].device_count) {
845             bool matches = true;
846             for (uint32_t k = 0; k < icd_phys_devs_array[j].device_count; k++) {
847                 if (icd_phys_devs_array[j].physical_devices[k] != next_icd_phys_devs->physical_devices[k]) {
848                     matches = false;
849                     break;
850                 }
851             }
852             if (matches) {
853                 already_enumerated = true;
854             }
855         }
856     }
857     if (!already_enumerated) {
858         next_icd_phys_devs->device_count = count;
859         next_icd_phys_devs->icd_index = icd_idx;
860         next_icd_phys_devs->icd_term = icd_term;
861         next_icd_phys_devs->windows_adapter_luid = luid;
862         (*icd_phys_devs_array_count)++;
863     }
864 
865     return VK_SUCCESS;
866 }
867 
868 // Whenever there are multiple drivers for the same hardware and one of the drivers is an implementation layered on top of another
869 // API (such as the Dozen driver which converts vulkan to Dx12), we want to make sure the layered driver appears after the 'native'
870 // driver. This function iterates over all physical devices and make sure any with matching LUID's are sorted such that drivers with
871 // a underlyingAPI of VK_LAYERED_DRIVER_UNDERLYING_API_D3D12_MSFT are ordered after drivers without it.
sort_physical_devices_with_same_luid(struct loader_instance *inst, uint32_t icd_phys_devs_array_count, struct loader_icd_physical_devices *icd_phys_devs_array)872 void sort_physical_devices_with_same_luid(struct loader_instance *inst, uint32_t icd_phys_devs_array_count,
873                                           struct loader_icd_physical_devices *icd_phys_devs_array) {
874     bool app_is_vulkan_1_1 = loader_check_version_meets_required(LOADER_VERSION_1_1_0, inst->app_api_version);
875 
876     for (uint32_t i = 0; icd_phys_devs_array_count > 1 && i < icd_phys_devs_array_count - 1; i++) {
877         for (uint32_t j = i + 1; j < icd_phys_devs_array_count; j++) {
878             // Only want to reorder physical devices if their ICD's LUID's match
879             if ((icd_phys_devs_array[i].windows_adapter_luid.HighPart != icd_phys_devs_array[j].windows_adapter_luid.HighPart) ||
880                 (icd_phys_devs_array[i].windows_adapter_luid.LowPart != icd_phys_devs_array[j].windows_adapter_luid.LowPart)) {
881                 continue;
882             }
883 
884             VkLayeredDriverUnderlyingApiMSFT underlyingAPI = VK_LAYERED_DRIVER_UNDERLYING_API_NONE_MSFT;
885             VkPhysicalDeviceLayeredDriverPropertiesMSFT layered_driver_properties_msft = {0};
886             layered_driver_properties_msft.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LAYERED_DRIVER_PROPERTIES_MSFT;
887             VkPhysicalDeviceProperties2 props2 = {0};
888             props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
889             props2.pNext = (void *)&layered_driver_properties_msft;
890 
891             // Because there may be multiple physical devices associated with each ICD, we need to check each physical device
892             // whether it is layered
893             for (uint32_t k = 0; k < icd_phys_devs_array[i].device_count; k++) {
894                 VkPhysicalDeviceProperties dev_props = {0};
895                 icd_phys_devs_array[i].icd_term->dispatch.GetPhysicalDeviceProperties(icd_phys_devs_array[i].physical_devices[k],
896                                                                                       &dev_props);
897 
898                 bool device_is_1_1_capable =
899                     loader_check_version_meets_required(LOADER_VERSION_1_1_0, loader_make_version(dev_props.apiVersion));
900 
901                 PFN_vkGetPhysicalDeviceProperties2 GetPhysDevProps2 = NULL;
902                 if (app_is_vulkan_1_1 && device_is_1_1_capable) {
903                     GetPhysDevProps2 = icd_phys_devs_array[i].icd_term->dispatch.GetPhysicalDeviceProperties2;
904                 } else {
905                     GetPhysDevProps2 = (PFN_vkGetPhysicalDeviceProperties2)icd_phys_devs_array[i]
906                                            .icd_term->dispatch.GetPhysicalDeviceProperties2KHR;
907                 }
908                 if (GetPhysDevProps2) {
909                     GetPhysDevProps2(icd_phys_devs_array[i].physical_devices[k], &props2);
910                     if (layered_driver_properties_msft.underlyingAPI != VK_LAYERED_DRIVER_UNDERLYING_API_NONE_MSFT) {
911                         underlyingAPI = layered_driver_properties_msft.underlyingAPI;
912                         break;
913                     }
914                 }
915             }
916             if (underlyingAPI == VK_LAYERED_DRIVER_UNDERLYING_API_D3D12_MSFT) {
917                 struct loader_icd_physical_devices swap_icd = icd_phys_devs_array[i];
918                 icd_phys_devs_array[i] = icd_phys_devs_array[j];
919                 icd_phys_devs_array[j] = swap_icd;
920             }
921         }
922     }
923 }
924 
925 // This function allocates icd_phys_devs_array which must be freed by the caller if not null
windows_read_sorted_physical_devices(struct loader_instance *inst, uint32_t *icd_phys_devs_array_count, struct loader_icd_physical_devices **icd_phys_devs_array)926 VkResult windows_read_sorted_physical_devices(struct loader_instance *inst, uint32_t *icd_phys_devs_array_count,
927                                               struct loader_icd_physical_devices **icd_phys_devs_array) {
928     VkResult res = VK_SUCCESS;
929 
930     uint32_t icd_phys_devs_array_size = 0;
931     struct loader_icd_term *icd_term = NULL;
932     IDXGIFactory6 *dxgi_factory = NULL;
933     HRESULT hres = fpCreateDXGIFactory1(&IID_IDXGIFactory6, (void **)&dxgi_factory);
934     if (hres != S_OK) {
935         loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Failed to create DXGI factory 6. Physical devices will not be sorted");
936         goto out;
937     }
938     icd_phys_devs_array_size = 16;
939     *icd_phys_devs_array = loader_instance_heap_calloc(inst, icd_phys_devs_array_size * sizeof(struct loader_icd_physical_devices),
940                                                        VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
941     if (*icd_phys_devs_array == NULL) {
942         res = VK_ERROR_OUT_OF_HOST_MEMORY;
943         goto out;
944     }
945 
946     for (uint32_t i = 0;; ++i) {
947         IDXGIAdapter1 *adapter;
948         hres = dxgi_factory->lpVtbl->EnumAdapterByGpuPreference(dxgi_factory, i, DXGI_GPU_PREFERENCE_UNSPECIFIED,
949                                                                 &IID_IDXGIAdapter1, (void **)&adapter);
950         if (hres == DXGI_ERROR_NOT_FOUND) {
951             break;  // No more adapters
952         } else if (hres != S_OK) {
953             loader_log(inst, VULKAN_LOADER_WARN_BIT, 0,
954                        "Failed to enumerate adapters by GPU preference at index %u. This adapter will not be sorted", i);
955             break;
956         }
957 
958         DXGI_ADAPTER_DESC1 description;
959         hres = adapter->lpVtbl->GetDesc1(adapter, &description);
960         if (hres != S_OK) {
961             loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Failed to get adapter LUID index %u. This adapter will not be sorted", i);
962             continue;
963         }
964 
965         if (icd_phys_devs_array_size <= i) {
966             uint32_t old_size = icd_phys_devs_array_size * sizeof(struct loader_icd_physical_devices);
967             *icd_phys_devs_array = loader_instance_heap_realloc(inst, *icd_phys_devs_array, old_size, 2 * old_size,
968                                                                 VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
969             if (*icd_phys_devs_array == NULL) {
970                 adapter->lpVtbl->Release(adapter);
971                 res = VK_ERROR_OUT_OF_HOST_MEMORY;
972                 goto out;
973             }
974             icd_phys_devs_array_size *= 2;
975         }
976         (*icd_phys_devs_array)[*icd_phys_devs_array_count].device_count = 0;
977         (*icd_phys_devs_array)[*icd_phys_devs_array_count].physical_devices = NULL;
978 
979         icd_term = inst->icd_terms;
980         for (uint32_t icd_idx = 0; NULL != icd_term; icd_term = icd_term->next, icd_idx++) {
981             // This is the new behavior, which cannot be run unless the ICD provides EnumerateAdapterPhysicalDevices
982             if (icd_term->scanned_icd->EnumerateAdapterPhysicalDevices == NULL) {
983                 continue;
984             }
985 
986             res = enumerate_adapter_physical_devices(inst, icd_term, icd_idx, description.AdapterLuid, icd_phys_devs_array_count,
987                                                      *icd_phys_devs_array);
988             if (res == VK_ERROR_OUT_OF_HOST_MEMORY) {
989                 adapter->lpVtbl->Release(adapter);
990                 goto out;
991             }
992         }
993 
994         adapter->lpVtbl->Release(adapter);
995     }
996 
997     dxgi_factory->lpVtbl->Release(dxgi_factory);
998 
999     sort_physical_devices_with_same_luid(inst, *icd_phys_devs_array_count, *icd_phys_devs_array);
1000 
1001 out:
1002     if (*icd_phys_devs_array_count == 0 && *icd_phys_devs_array != NULL) {
1003         loader_instance_heap_free(inst, *icd_phys_devs_array);
1004         *icd_phys_devs_array = NULL;
1005     }
1006     return res;
1007 }
1008 
windows_initialize_dxgi(void)1009 VkLoaderFeatureFlags windows_initialize_dxgi(void) {
1010     VkLoaderFeatureFlags feature_flags = 0;
1011     IDXGIFactory6 *dxgi_factory = NULL;
1012     HRESULT hres = fpCreateDXGIFactory1(&IID_IDXGIFactory6, (void **)&dxgi_factory);
1013     if (hres == S_OK) {
1014         feature_flags |= VK_LOADER_FEATURE_PHYSICAL_DEVICE_SORTING;
1015         dxgi_factory->lpVtbl->Release(dxgi_factory);
1016     }
1017     return feature_flags;
1018 }
1019 
1020 // Sort the VkPhysicalDevices that are part of the current group with the list passed in from the sorted list.
1021 // Multiple groups could have devices out of the same sorted list, however, a single group's devices must all come
1022 // from the same sorted list.
windows_sort_devices_in_group(struct loader_instance *inst, struct VkPhysicalDeviceGroupProperties *group_props, struct loader_icd_physical_devices *icd_sorted_list)1023 void windows_sort_devices_in_group(struct loader_instance *inst, struct VkPhysicalDeviceGroupProperties *group_props,
1024                                    struct loader_icd_physical_devices *icd_sorted_list) {
1025     uint32_t cur_index = 0;
1026     for (uint32_t dev = 0; dev < icd_sorted_list->device_count; ++dev) {
1027         for (uint32_t grp_dev = cur_index; grp_dev < group_props->physicalDeviceCount; ++grp_dev) {
1028             if (icd_sorted_list->physical_devices[dev] == group_props->physicalDevices[grp_dev]) {
1029                 if (cur_index != grp_dev) {
1030                     VkPhysicalDevice swap_dev = group_props->physicalDevices[cur_index];
1031                     group_props->physicalDevices[cur_index] = group_props->physicalDevices[grp_dev];
1032                     group_props->physicalDevices[grp_dev] = swap_dev;
1033                 }
1034                 cur_index++;
1035                 break;
1036             }
1037         }
1038     }
1039     if (cur_index == 0) {
1040         loader_log(inst, VULKAN_LOADER_WARN_BIT, 0,
1041                    "windows_sort_devices_in_group:  Never encountered a device in the sorted list group");
1042     }
1043 }
1044 
1045 // This function sorts an array in physical device groups based on the sorted physical device information
windows_sort_physical_device_groups(struct loader_instance *inst, const uint32_t group_count, struct loader_physical_device_group_term *sorted_group_term, const uint32_t sorted_device_count, struct loader_icd_physical_devices *sorted_phys_dev_array)1046 VkResult windows_sort_physical_device_groups(struct loader_instance *inst, const uint32_t group_count,
1047                                              struct loader_physical_device_group_term *sorted_group_term,
1048                                              const uint32_t sorted_device_count,
1049                                              struct loader_icd_physical_devices *sorted_phys_dev_array) {
1050     if (0 == group_count || NULL == sorted_group_term) {
1051         loader_log(inst, VULKAN_LOADER_WARN_BIT, 0,
1052                    "windows_sort_physical_device_groups: Called with invalid information (Group count %d, Sorted Info %p)",
1053                    group_count, sorted_group_term);
1054         return VK_ERROR_INITIALIZATION_FAILED;
1055     }
1056 
1057     uint32_t new_index = 0;
1058     for (uint32_t icd = 0; icd < sorted_device_count; ++icd) {
1059         for (uint32_t dev = 0; dev < sorted_phys_dev_array[icd].device_count; ++dev) {
1060             // Find a group associated with a given device
1061             for (uint32_t group = new_index; group < group_count; ++group) {
1062                 bool device_found = false;
1063                 // Look for the current sorted device in a group and put it in the correct location if it isn't already
1064                 for (uint32_t grp_dev = 0; grp_dev < sorted_group_term[group].group_props.physicalDeviceCount; ++grp_dev) {
1065                     if (sorted_group_term[group].group_props.physicalDevices[grp_dev] ==
1066                         sorted_phys_dev_array[icd].physical_devices[dev]) {
1067                         // First, sort devices inside of group to be in priority order
1068                         windows_sort_devices_in_group(inst, &sorted_group_term[group].group_props, &sorted_phys_dev_array[icd]);
1069 
1070                         // Second, move the group up in priority if it needs to be
1071                         if (new_index != group) {
1072                             struct loader_physical_device_group_term tmp = sorted_group_term[new_index];
1073                             sorted_group_term[new_index] = sorted_group_term[group];
1074                             sorted_group_term[group] = tmp;
1075                         }
1076                         device_found = true;
1077                         new_index++;
1078                         break;
1079                     }
1080                 }
1081                 if (device_found) {
1082                     break;
1083                 }
1084             }
1085         }
1086     }
1087     return VK_SUCCESS;
1088 }
1089 
windows_get_app_package_manifest_path(const struct loader_instance *inst)1090 char *windows_get_app_package_manifest_path(const struct loader_instance *inst) {
1091     // These functions are only available on Windows 8 and above, load them dynamically for compatibility with Windows 7
1092     typedef LONG(WINAPI * PFN_GetPackagesByPackageFamily)(PCWSTR, UINT32 *, PWSTR *, UINT32 *, WCHAR *);
1093     PFN_GetPackagesByPackageFamily fpGetPackagesByPackageFamily =
1094         (PFN_GetPackagesByPackageFamily)(void *)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetPackagesByPackageFamily");
1095     if (!fpGetPackagesByPackageFamily) {
1096         return NULL;
1097     }
1098     typedef LONG(WINAPI * PFN_GetPackagePathByFullName)(PCWSTR, UINT32 *, PWSTR);
1099     PFN_GetPackagePathByFullName fpGetPackagePathByFullName =
1100         (PFN_GetPackagePathByFullName)(void *)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetPackagePathByFullName");
1101     if (!fpGetPackagePathByFullName) {
1102         return NULL;
1103     }
1104 
1105     UINT32 numPackages = 0, bufferLength = 0;
1106     /* This literal string identifies the Microsoft-published OpenCL and OpenGL Compatibility Pack
1107      * (so named at the time this is being added), which contains OpenGLOn12 and OpenCLOn12 mapping
1108      * layers, and will contain VulkanOn12 (aka Dozen) going forward.
1109      */
1110     PCWSTR familyName = L"Microsoft.D3DMappingLayers_8wekyb3d8bbwe";
1111     if (ERROR_INSUFFICIENT_BUFFER != fpGetPackagesByPackageFamily(familyName, &numPackages, NULL, &bufferLength, NULL) ||
1112         numPackages == 0 || bufferLength == 0) {
1113         loader_log(inst, VULKAN_LOADER_INFO_BIT, 0,
1114                    "windows_get_app_package_manifest_path: Failed to find mapping layers packages by family name");
1115         return NULL;
1116     }
1117 
1118     char *ret = NULL;
1119     WCHAR *buffer = loader_instance_heap_alloc(inst, sizeof(WCHAR) * bufferLength, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
1120     PWSTR *packages = loader_instance_heap_alloc(inst, sizeof(PWSTR) * numPackages, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
1121     if (!buffer || !packages) {
1122         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
1123                    "windows_get_app_package_manifest_path: Failed to allocate memory for package names");
1124         goto cleanup;
1125     }
1126 
1127     if (ERROR_SUCCESS != fpGetPackagesByPackageFamily(familyName, &numPackages, packages, &bufferLength, buffer)) {
1128         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
1129                    "windows_get_app_package_manifest_path: Failed to mapping layers package full names");
1130         goto cleanup;
1131     }
1132 
1133     UINT32 pathLength = 0;
1134     WCHAR path[MAX_PATH];
1135     memset(path, 0, sizeof(path));
1136     if (ERROR_INSUFFICIENT_BUFFER != fpGetPackagePathByFullName(packages[0], &pathLength, NULL) || pathLength > MAX_PATH ||
1137         ERROR_SUCCESS != fpGetPackagePathByFullName(packages[0], &pathLength, path)) {
1138         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
1139                    "windows_get_app_package_manifest_path: Failed to get mapping layers package path");
1140         goto cleanup;
1141     }
1142 
1143     int narrowPathLength = WideCharToMultiByte(CP_ACP, 0, path, -1, NULL, 0, NULL, NULL);
1144     if (narrowPathLength == 0) {
1145         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
1146                    "windows_get_app_package_manifest_path: Failed to convert path from wide to narrow");
1147         goto cleanup;
1148     }
1149 
1150     ret = loader_instance_heap_alloc(inst, narrowPathLength, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
1151     if (!ret) {
1152         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "windows_get_app_package_manifest_path: Failed to allocate path");
1153         goto cleanup;
1154     }
1155 
1156     narrowPathLength = WideCharToMultiByte(CP_ACP, 0, path, -1, ret, narrowPathLength, NULL, NULL);
1157     assert((size_t)narrowPathLength == strlen(ret) + 1);
1158 
1159 cleanup:
1160     loader_instance_heap_free(inst, buffer);
1161     loader_instance_heap_free(inst, packages);
1162     return ret;
1163 }
1164 
get_settings_path_if_exists_in_registry_key(const struct loader_instance *inst, char **out_path, HKEY key)1165 VkResult get_settings_path_if_exists_in_registry_key(const struct loader_instance *inst, char **out_path, HKEY key) {
1166     VkResult result = VK_ERROR_INITIALIZATION_FAILED;
1167 
1168     char name[MAX_STRING_SIZE] = {0};
1169     DWORD name_size = sizeof(name);
1170 
1171     *out_path = NULL;
1172 
1173     LONG rtn_value = ERROR_SUCCESS;
1174     for (DWORD idx = 0; rtn_value == ERROR_SUCCESS; idx++) {
1175         DWORD value = 0;
1176         DWORD value_size = sizeof(value);
1177         rtn_value = RegEnumValue(key, idx, name, &name_size, NULL, NULL, (LPBYTE)&value, &value_size);
1178 
1179         if (ERROR_SUCCESS != rtn_value) {
1180             break;
1181         }
1182 
1183         uint32_t start_of_path_filename = 0;
1184         for (uint32_t last_char = name_size; last_char > 0; last_char--) {
1185             if (name[last_char] == '\\') {
1186                 start_of_path_filename = last_char + 1;
1187                 break;
1188             }
1189         }
1190 
1191         // Make sure the path exists first
1192         if (*out_path && !loader_platform_file_exists(name)) {
1193             return VK_ERROR_INITIALIZATION_FAILED;
1194         }
1195 
1196         if (strcmp(VK_LOADER_SETTINGS_FILENAME, &(name[start_of_path_filename])) == 0) {
1197             *out_path = loader_instance_heap_calloc(inst, name_size + 1, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
1198             if (*out_path == NULL) {
1199                 return VK_ERROR_OUT_OF_HOST_MEMORY;
1200             }
1201             loader_strncpy(*out_path, name_size + 1, name, name_size);
1202             (*out_path)[name_size] = '\0';
1203             result = VK_SUCCESS;
1204             break;
1205         }
1206     }
1207 
1208     return result;
1209 }
1210 
windows_get_loader_settings_file_path(const struct loader_instance *inst, char **out_path)1211 VkResult windows_get_loader_settings_file_path(const struct loader_instance *inst, char **out_path) {
1212     VkResult result = VK_SUCCESS;
1213     DWORD access_flags = KEY_QUERY_VALUE;
1214     HKEY key = NULL;
1215 
1216     *out_path = NULL;
1217 
1218     // if we are running with admin privileges, only check HKEY_LOCAL_MACHINE.
1219     // Otherwise check HKEY_CURRENT_USER, and if nothing is there, look in HKEY_LOCAL_MACHINE
1220 
1221     if (is_high_integrity()) {
1222         LONG rtn_value = RegOpenKeyEx(HKEY_LOCAL_MACHINE, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key);
1223         if (ERROR_SUCCESS != rtn_value) {
1224             result = VK_ERROR_FEATURE_NOT_PRESENT;
1225             goto out;
1226         }
1227         result = get_settings_path_if_exists_in_registry_key(inst, out_path, key);
1228     } else {
1229         LONG rtn_value = RegOpenKeyEx(HKEY_CURRENT_USER, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key);
1230         if (ERROR_SUCCESS == rtn_value) {
1231             result = get_settings_path_if_exists_in_registry_key(inst, out_path, key);
1232             RegCloseKey(key);
1233             // Either we got OOM and *must* exit or we successfully found the settings file and can exit
1234             if (result == VK_ERROR_OUT_OF_HOST_MEMORY || result == VK_SUCCESS) {
1235                 goto out;
1236             }
1237         }
1238 
1239         rtn_value = RegOpenKeyEx(HKEY_LOCAL_MACHINE, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key);
1240         if (ERROR_SUCCESS != rtn_value) {
1241             result = VK_ERROR_FEATURE_NOT_PRESENT;
1242             goto out;
1243         }
1244 
1245         result = get_settings_path_if_exists_in_registry_key(inst, out_path, key);
1246         if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
1247             goto out;
1248         }
1249     }
1250 
1251 out:
1252     if (NULL != key) {
1253         RegCloseKey(key);
1254     }
1255 
1256     return result;
1257 }
1258 
1259 #endif  // _WIN32
1260