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