• 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 #ifdef _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 "get_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 #ifdef __MINGW32__
49 #undef strcpy  // fix error with redfined strcpy when building with MinGW-w64
50 #endif
51 #include <dxgi1_6.h>
52 #include "adapters.h"
53 
54 #ifndef __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 static PFN_CreateDXGIFactory1 fpCreateDXGIFactory1;
65 
windows_initialization(void)66 void windows_initialization(void) {
67     char dll_location[MAX_PATH];
68     HMODULE module_handle = NULL;
69 
70     // Get a module handle to a static function inside of this source
71     if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
72                           (LPCSTR)&loader_debug_init, &module_handle) != 0 &&
73         GetModuleFileName(module_handle, dll_location, sizeof(dll_location)) != 0) {
74         loader_log(NULL, VULKAN_LOADER_INFO_BIT, 0, "Using Vulkan Loader %s", dll_location);
75     }
76 
77     // This is needed to ensure that newer APIs are available right away
78     // and not after the first call that has been statically linked
79     LoadLibrary("gdi32.dll");
80 
81     wchar_t systemPath[MAX_PATH] = L"";
82     GetSystemDirectoryW(systemPath, MAX_PATH);
83     StringCchCatW(systemPath, MAX_PATH, L"\\dxgi.dll");
84     HMODULE dxgi_module = LoadLibraryW(systemPath);
85     fpCreateDXGIFactory1 = dxgi_module == NULL ? NULL : (PFN_CreateDXGIFactory1)GetProcAddress(dxgi_module, "CreateDXGIFactory1");
86 
87 #if !defined(NDEBUG)
88     _set_error_mode(_OUT_TO_STDERR);
89     _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
90     _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
91 #endif
92 }
93 
DllMain(HINSTANCE hinst,DWORD reason,LPVOID reserved)94 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) {
95     switch (reason) {
96         case DLL_PROCESS_ATTACH:
97             loader_initialize();
98             break;
99         case DLL_PROCESS_DETACH:
100             if (NULL == reserved) {
101                 loader_release();
102             }
103             break;
104         default:
105             // Do nothing
106             break;
107     }
108     return TRUE;
109 }
110 
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)111 bool windows_add_json_entry(const struct loader_instance *inst,
112                             char **reg_data,    // list of JSON files
113                             PDWORD total_size,  // size of reg_data
114                             LPCSTR key_name,    // key name - used for debug prints - i.e. VulkanDriverName
115                             DWORD key_type,     // key data type
116                             LPSTR json_path,    // JSON string to add to the list reg_data
117                             DWORD json_size,    // size in bytes of json_path
118                             VkResult *result) {
119     // Check for and ignore duplicates.
120     if (*reg_data && strstr(*reg_data, json_path)) {
121         // Success. The json_path is already in the list.
122         return true;
123     }
124 
125     if (NULL == *reg_data) {
126         *reg_data = loader_instance_heap_alloc(inst, *total_size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
127         if (NULL == *reg_data) {
128             loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
129                        "windows_add_json_entry: Failed to allocate space for registry data for key %s", json_path);
130             *result = VK_ERROR_OUT_OF_HOST_MEMORY;
131             return false;
132         }
133         *reg_data[0] = '\0';
134     } else if (strlen(*reg_data) + json_size + 1 > *total_size) {
135         void *new_ptr =
136             loader_instance_heap_realloc(inst, *reg_data, *total_size, *total_size * 2, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
137         if (NULL == new_ptr) {
138             loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
139                        "windows_add_json_entry: Failed to reallocate space for registry value of size %d for key %s",
140                        *total_size * 2, json_path);
141             *result = VK_ERROR_OUT_OF_HOST_MEMORY;
142             return false;
143         }
144         *reg_data = new_ptr;
145         *total_size *= 2;
146     }
147 
148     for (char *curr_filename = json_path; curr_filename[0] != '\0'; curr_filename += strlen(curr_filename) + 1) {
149         if (strlen(*reg_data) == 0) {
150             (void)snprintf(*reg_data, json_size + 1, "%s", curr_filename);
151         } else {
152             (void)snprintf(*reg_data + strlen(*reg_data), json_size + 2, "%c%s", PATH_SEPARATOR, curr_filename);
153         }
154         loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "%s: Located json file \"%s\" from PnP registry: %s", __FUNCTION__,
155                    curr_filename, key_name);
156 
157         if (key_type == REG_SZ) {
158             break;
159         }
160     }
161     return true;
162 }
163 
windows_get_device_registry_entry(const struct loader_instance * inst,char ** reg_data,PDWORD total_size,DEVINST dev_id,LPCSTR value_name,VkResult * result)164 bool windows_get_device_registry_entry(const struct loader_instance *inst, char **reg_data, PDWORD total_size, DEVINST dev_id,
165                                        LPCSTR value_name, VkResult *result) {
166     HKEY hkrKey = INVALID_HANDLE_VALUE;
167     DWORD requiredSize, data_type;
168     char *manifest_path = NULL;
169     bool found = false;
170 
171     assert(reg_data != NULL && "windows_get_device_registry_entry: reg_data is a NULL pointer");
172     assert(total_size != NULL && "windows_get_device_registry_entry: total_size is a NULL pointer");
173 
174     CONFIGRET status = CM_Open_DevNode_Key(dev_id, KEY_QUERY_VALUE, 0, RegDisposition_OpenExisting, &hkrKey, CM_REGISTRY_SOFTWARE);
175     if (status != CR_SUCCESS) {
176         loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
177                    "windows_get_device_registry_entry: Failed to open registry key for DeviceID(%d)", dev_id);
178         *result = VK_ERROR_INCOMPATIBLE_DRIVER;
179         return false;
180     }
181 
182     // query value
183     LSTATUS ret = RegQueryValueEx(hkrKey, value_name, NULL, NULL, NULL, &requiredSize);
184 
185     if (ret != ERROR_SUCCESS) {
186         if (ret == ERROR_FILE_NOT_FOUND) {
187             loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
188                        "windows_get_device_registry_entry: Device ID(%d) Does not contain a value for \"%s\"", dev_id, value_name);
189         } else {
190             loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
191                        "windows_get_device_registry_entry: DeviceID(%d) Failed to obtain %s size", dev_id, value_name);
192         }
193         goto out;
194     }
195 
196     manifest_path = loader_instance_heap_alloc(inst, requiredSize, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
197     if (manifest_path == NULL) {
198         loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
199                    "windows_get_device_registry_entry: Failed to allocate space for DriverName.");
200         *result = VK_ERROR_OUT_OF_HOST_MEMORY;
201         goto out;
202     }
203 
204     ret = RegQueryValueEx(hkrKey, value_name, NULL, &data_type, (BYTE *)manifest_path, &requiredSize);
205 
206     if (ret != ERROR_SUCCESS) {
207         loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
208                    "windows_get_device_registry_entry: DeviceID(%d) Failed to obtain %s", value_name);
209         *result = VK_ERROR_INCOMPATIBLE_DRIVER;
210         goto out;
211     }
212 
213     if (data_type != REG_SZ && data_type != REG_MULTI_SZ) {
214         loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
215                    "windows_get_device_registry_entry: Invalid %s data type. Expected REG_SZ or REG_MULTI_SZ.", value_name);
216         *result = VK_ERROR_INCOMPATIBLE_DRIVER;
217         goto out;
218     }
219 
220     found = windows_add_json_entry(inst, reg_data, total_size, value_name, data_type, manifest_path, requiredSize, result);
221 
222 out:
223     loader_instance_heap_free(inst, manifest_path);
224     RegCloseKey(hkrKey);
225     return found;
226 }
227 
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)228 VkResult windows_get_device_registry_files(const struct loader_instance *inst, uint32_t log_target_flag, char **reg_data,
229                                            PDWORD reg_data_size, LPCSTR value_name) {
230     static const wchar_t *softwareComponentGUID = L"{5c4c3332-344d-483c-8739-259e934c9cc8}";
231     static const wchar_t *displayGUID = L"{4d36e968-e325-11ce-bfc1-08002be10318}";
232 #ifdef CM_GETIDLIST_FILTER_PRESENT
233     const ULONG flags = CM_GETIDLIST_FILTER_CLASS | CM_GETIDLIST_FILTER_PRESENT;
234 #else
235     const ULONG flags = 0x300;
236 #endif
237 
238     wchar_t childGuid[MAX_GUID_STRING_LEN + 2];  // +2 for brackets {}
239     for (uint32_t i = 0; i < MAX_GUID_STRING_LEN + 2; i++) {
240         childGuid[i] = L'\0';
241     }
242     ULONG childGuidSize = sizeof(childGuid);
243 
244     DEVINST devID = 0, childID = 0;
245     wchar_t *pDeviceNames = NULL;
246     ULONG deviceNamesSize = 0;
247     VkResult result = VK_SUCCESS;
248     bool found = false;
249 
250     assert(reg_data != NULL && "windows_get_device_registry_files: reg_data is NULL");
251 
252     // if after obtaining the DeviceNameSize, new device is added start over
253     do {
254         CM_Get_Device_ID_List_SizeW(&deviceNamesSize, displayGUID, flags);
255 
256         loader_instance_heap_free(inst, pDeviceNames);
257 
258         pDeviceNames = loader_instance_heap_alloc(inst, deviceNamesSize * sizeof(wchar_t), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
259         if (pDeviceNames == NULL) {
260             loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
261                        "windows_get_device_registry_files: Failed to allocate space for display device names.");
262             result = VK_ERROR_OUT_OF_HOST_MEMORY;
263             return result;
264         }
265     } while (CM_Get_Device_ID_ListW(displayGUID, pDeviceNames, deviceNamesSize, flags) == CR_BUFFER_SMALL);
266 
267     if (pDeviceNames) {
268         for (wchar_t *deviceName = pDeviceNames; *deviceName; deviceName += wcslen(deviceName) + 1) {
269             CONFIGRET status = CM_Locate_DevNodeW(&devID, deviceName, CM_LOCATE_DEVNODE_NORMAL);
270             if (CR_SUCCESS != status) {
271                 loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
272                            "windows_get_device_registry_files: failed to open DevNode %ls", deviceName);
273                 continue;
274             }
275             ULONG ulStatus, ulProblem;
276             status = CM_Get_DevNode_Status(&ulStatus, &ulProblem, devID, 0);
277 
278             if (CR_SUCCESS != status) {
279                 loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
280                            "windows_get_device_registry_files: failed to probe device status %ls", deviceName);
281                 continue;
282             }
283             if ((ulStatus & DN_HAS_PROBLEM) && (ulProblem == CM_PROB_NEED_RESTART || ulProblem == DN_NEED_RESTART)) {
284                 loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
285                            "windows_get_device_registry_files: device %ls is pending reboot, skipping ...", deviceName);
286                 continue;
287             }
288 
289             loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0, "windows_get_device_registry_files: opening device %ls",
290                        deviceName);
291 
292             if (windows_get_device_registry_entry(inst, reg_data, reg_data_size, devID, value_name, &result)) {
293                 found = true;
294                 continue;
295             } else if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
296                 break;
297             }
298 
299             status = CM_Get_Child(&childID, devID, 0);
300             if (status != CR_SUCCESS) {
301                 loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
302                            "windows_get_device_registry_files: unable to open child-device error:%d", status);
303                 continue;
304             }
305 
306             do {
307                 wchar_t buffer[MAX_DEVICE_ID_LEN];
308                 CM_Get_Device_IDW(childID, buffer, MAX_DEVICE_ID_LEN, 0);
309 
310                 loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
311                            "windows_get_device_registry_files: Opening child device %d - %ls", childID, buffer);
312 
313                 status = CM_Get_DevNode_Registry_PropertyW(childID, CM_DRP_CLASSGUID, NULL, &childGuid, &childGuidSize, 0);
314                 if (status != CR_SUCCESS) {
315                     loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
316                                "windows_get_device_registry_files: unable to obtain GUID for:%d error:%d", childID, status);
317 
318                     result = VK_ERROR_INCOMPATIBLE_DRIVER;
319                     continue;
320                 }
321 
322                 if (wcscmp(childGuid, softwareComponentGUID) != 0) {
323                     loader_log(inst, VULKAN_LOADER_DEBUG_BIT | log_target_flag, 0,
324                                "windows_get_device_registry_files: GUID for %d is not SoftwareComponent skipping", childID);
325                     continue;
326                 }
327 
328                 if (windows_get_device_registry_entry(inst, reg_data, reg_data_size, childID, value_name, &result)) {
329                     found = true;
330                     break;  // check next-display-device
331                 }
332 
333             } while (CM_Get_Sibling(&childID, childID, 0) == CR_SUCCESS);
334         }
335 
336         loader_instance_heap_free(inst, pDeviceNames);
337     }
338 
339     if (!found && result != VK_ERROR_OUT_OF_HOST_MEMORY) {
340         loader_log(inst, log_target_flag, 0, "windows_get_device_registry_files: found no registry files");
341         result = VK_ERROR_INCOMPATIBLE_DRIVER;
342     }
343 
344     return result;
345 }
346 
windows_get_registry_files(const struct loader_instance * inst,char * location,bool use_secondary_hive,char ** reg_data,PDWORD reg_data_size)347 VkResult windows_get_registry_files(const struct loader_instance *inst, char *location, bool use_secondary_hive, char **reg_data,
348                                     PDWORD reg_data_size) {
349     // This list contains all of the allowed ICDs. This allows us to verify that a device is actually present from the vendor
350     // specified. This does disallow other vendors, but any new driver should use the device-specific registries anyway.
351     static const struct {
352         const char *filename;
353         unsigned int vendor_id;
354     } known_drivers[] = {
355 #if defined(_WIN64)
356         {
357             .filename = "igvk64.json",
358             .vendor_id = 0x8086,
359         },
360         {
361             .filename = "nv-vk64.json",
362             .vendor_id = 0x10de,
363         },
364         {
365             .filename = "amd-vulkan64.json",
366             .vendor_id = 0x1002,
367         },
368         {
369             .filename = "amdvlk64.json",
370             .vendor_id = 0x1002,
371         },
372 #else
373         {
374             .filename = "igvk32.json",
375             .vendor_id = 0x8086,
376         },
377         {
378             .filename = "nv-vk32.json",
379             .vendor_id = 0x10de,
380         },
381         {
382             .filename = "amd-vulkan32.json",
383             .vendor_id = 0x1002,
384         },
385         {
386             .filename = "amdvlk32.json",
387             .vendor_id = 0x1002,
388         },
389 #endif
390     };
391 
392     LONG rtn_value;
393     HKEY hive = DEFAULT_VK_REGISTRY_HIVE, key;
394     DWORD access_flags;
395     char name[2048];
396     char *loc = location;
397     char *next;
398     DWORD name_size = sizeof(name);
399     DWORD value;
400     DWORD value_size = sizeof(value);
401     VkResult result = VK_SUCCESS;
402     bool found = false;
403     IDXGIFactory1 *dxgi_factory = NULL;
404     bool is_driver = !strcmp(location, VK_DRIVERS_INFO_REGISTRY_LOC);
405     uint32_t log_target_flag = is_driver ? VULKAN_LOADER_DRIVER_BIT : VULKAN_LOADER_LAYER_BIT;
406 
407     assert(reg_data != NULL && "windows_get_registry_files: reg_data is a NULL pointer");
408 
409     if (is_driver) {
410         HRESULT hres = fpCreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&dxgi_factory);
411         if (hres != S_OK) {
412             loader_log(inst, VULKAN_LOADER_WARN_BIT | log_target_flag, 0,
413                        "windows_get_registry_files: Failed to create dxgi factory for ICD registry verification. No ICDs will be "
414                        "added from "
415                        "legacy registry locations");
416             goto out;
417         }
418     }
419 
420     while (*loc) {
421         next = loader_get_next_path(loc);
422         access_flags = KEY_QUERY_VALUE;
423         rtn_value = RegOpenKeyEx(hive, loc, 0, access_flags, &key);
424         if (ERROR_SUCCESS == rtn_value) {
425             for (DWORD idx = 0;
426                  (rtn_value = RegEnumValue(key, idx++, name, &name_size, NULL, NULL, (LPBYTE)&value, &value_size)) == ERROR_SUCCESS;
427                  name_size = sizeof(name), value_size = sizeof(value)) {
428                 if (value_size == sizeof(value) && value == 0) {
429                     if (NULL == *reg_data) {
430                         *reg_data = loader_instance_heap_alloc(inst, *reg_data_size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
431                         if (NULL == *reg_data) {
432                             loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
433                                        "windows_get_registry_files: Failed to allocate space for registry data for key %s", name);
434                             RegCloseKey(key);
435                             result = VK_ERROR_OUT_OF_HOST_MEMORY;
436                             goto out;
437                         }
438                         *reg_data[0] = '\0';
439                     } else if (strlen(*reg_data) + name_size + 1 > *reg_data_size) {
440                         void *new_ptr = loader_instance_heap_realloc(inst, *reg_data, *reg_data_size, *reg_data_size * 2,
441                                                                      VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
442                         if (NULL == new_ptr) {
443                             loader_log(
444                                 inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
445                                 "windows_get_registry_files: Failed to reallocate space for registry value of size %d for key %s",
446                                 *reg_data_size * 2, name);
447                             RegCloseKey(key);
448                             result = VK_ERROR_OUT_OF_HOST_MEMORY;
449                             goto out;
450                         }
451                         *reg_data = new_ptr;
452                         *reg_data_size *= 2;
453                     }
454 
455                     // We've now found a json file. If this is an ICD, we still need to check if there is actually a device
456                     // that matches this ICD
457                     loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
458                                "Located json file \"%s\" from registry \"%s\\%s\"", name,
459                                hive == DEFAULT_VK_REGISTRY_HIVE ? DEFAULT_VK_REGISTRY_HIVE_STR : SECONDARY_VK_REGISTRY_HIVE_STR,
460                                location);
461                     if (is_driver) {
462                         uint32_t i = 0;
463                         for (i = 0; i < sizeof(known_drivers) / sizeof(known_drivers[0]); ++i) {
464                             if (!strcmp(name + strlen(name) - strlen(known_drivers[i].filename), known_drivers[i].filename)) {
465                                 break;
466                             }
467                         }
468                         if (i == sizeof(known_drivers) / sizeof(known_drivers[0])) {
469                             loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
470                                        "Driver %s is not recognized as a known driver. It will be assumed to be active", name);
471                         } else {
472                             bool found_gpu = false;
473                             for (int j = 0;; ++j) {
474                                 IDXGIAdapter1 *adapter;
475                                 HRESULT hres = dxgi_factory->lpVtbl->EnumAdapters1(dxgi_factory, j, &adapter);
476                                 if (hres == DXGI_ERROR_NOT_FOUND) {
477                                     break;
478                                 } else if (hres != S_OK) {
479                                     loader_log(inst, VULKAN_LOADER_WARN_BIT | log_target_flag, 0,
480                                                "Failed to enumerate DXGI adapters at index %d. As a result, drivers may be skipped",
481                                                j);
482                                     continue;
483                                 }
484 
485                                 DXGI_ADAPTER_DESC1 description;
486                                 hres = adapter->lpVtbl->GetDesc1(adapter, &description);
487                                 if (hres != S_OK) {
488                                     loader_log(
489                                         inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
490                                         "Failed to get DXGI adapter information at index %d. As a result, drivers may be skipped",
491                                         j);
492                                     continue;
493                                 }
494 
495                                 if (description.VendorId == known_drivers[i].vendor_id) {
496                                     found_gpu = true;
497                                     break;
498                                 }
499                             }
500 
501                             if (!found_gpu) {
502                                 loader_log(inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
503                                            "Dropping driver %s as no corresponding DXGI adapter was found", name);
504                                 continue;
505                             }
506                         }
507                     }
508 
509                     if (strlen(*reg_data) == 0) {
510                         // The list is emtpy. Add the first entry.
511                         (void)snprintf(*reg_data, name_size + 1, "%s", name);
512                         found = true;
513                     } else {
514                         // At this point the reg_data variable contains other JSON paths, likely from the PNP/device section
515                         // of the registry that we want to have precedence over this non-device specific section of the registry.
516                         // To make sure we avoid enumerating old JSON files/drivers that might be present in the non-device specific
517                         // area of the registry when a newer device specific JSON file is present, do a check before adding.
518                         // Find the file name, without path, of the JSON file found in the non-device specific registry location.
519                         // If the same JSON file name is already found in the list, don't add it again.
520                         bool foundDuplicate = false;
521                         char *pLastSlashName = strrchr(name, '\\');
522                         if (pLastSlashName != NULL) {
523                             char *foundMatch = strstr(*reg_data, pLastSlashName + 1);
524                             if (foundMatch != NULL) {
525                                 foundDuplicate = true;
526                             }
527                         }
528 
529                         if (foundDuplicate == false) {
530                             // Add the new entry to the list.
531                             (void)snprintf(*reg_data + strlen(*reg_data), name_size + 2, "%c%s", PATH_SEPARATOR, name);
532                             found = true;
533                         } else {
534                             loader_log(
535                                 inst, VULKAN_LOADER_INFO_BIT | log_target_flag, 0,
536                                 "Skipping adding of json file \"%s\" from registry \"%s\\%s\" to the list due to duplication", name,
537                                 hive == DEFAULT_VK_REGISTRY_HIVE ? DEFAULT_VK_REGISTRY_HIVE_STR : SECONDARY_VK_REGISTRY_HIVE_STR,
538                                 location);
539                         }
540                     }
541                 }
542             }
543             RegCloseKey(key);
544         }
545 
546         // Advance the location - if the next location is in the secondary hive, then reset the locations and advance the hive
547         if (use_secondary_hive && (hive == DEFAULT_VK_REGISTRY_HIVE) && (*next == '\0')) {
548             loc = location;
549             hive = SECONDARY_VK_REGISTRY_HIVE;
550         } else {
551             loc = next;
552         }
553     }
554 
555     if (!found && result != VK_ERROR_OUT_OF_HOST_MEMORY) {
556         loader_log(inst, log_target_flag, 0, "Found no registry files in %s", location);
557         result = VK_ERROR_INCOMPATIBLE_DRIVER;
558     }
559 
560 out:
561     if (is_driver && dxgi_factory != NULL) {
562         dxgi_factory->lpVtbl->Release(dxgi_factory);
563     }
564 
565     return result;
566 }
567 
568 // 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)569 VkResult windows_read_manifest_from_d3d_adapters(const struct loader_instance *inst, char **reg_data, PDWORD reg_data_size,
570                                                  const wchar_t *value_name) {
571     VkResult result = VK_INCOMPLETE;
572     LoaderEnumAdapters2 adapters = {.adapter_count = 0, .adapters = NULL};
573     LoaderQueryRegistryInfo *full_info = NULL;
574     size_t full_info_size = 0;
575     char *json_path = NULL;
576     size_t json_path_size = 0;
577 
578     HMODULE gdi32_dll = GetModuleHandle("gdi32.dll");
579     if (gdi32_dll == NULL) {
580         result = VK_ERROR_INCOMPATIBLE_DRIVER;
581         goto out;
582     }
583 
584     PFN_LoaderEnumAdapters2 fpLoaderEnumAdapters2 = (PFN_LoaderEnumAdapters2)GetProcAddress(gdi32_dll, "D3DKMTEnumAdapters2");
585     PFN_LoaderQueryAdapterInfo fpLoaderQueryAdapterInfo =
586         (PFN_LoaderQueryAdapterInfo)GetProcAddress(gdi32_dll, "D3DKMTQueryAdapterInfo");
587     if (fpLoaderEnumAdapters2 == NULL || fpLoaderQueryAdapterInfo == NULL) {
588         result = VK_ERROR_INCOMPATIBLE_DRIVER;
589         goto out;
590     }
591 
592     // Get all of the adapters
593     NTSTATUS status = fpLoaderEnumAdapters2(&adapters);
594     if (status == STATUS_SUCCESS && adapters.adapter_count > 0) {
595         adapters.adapters = loader_instance_heap_alloc(inst, sizeof(*adapters.adapters) * adapters.adapter_count,
596                                                        VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
597         if (adapters.adapters == NULL) {
598             goto out;
599         }
600         status = fpLoaderEnumAdapters2(&adapters);
601     }
602     if (status != STATUS_SUCCESS) {
603         goto out;
604     }
605 
606     // If that worked, we need to get the manifest file(s) for each adapter
607     for (ULONG i = 0; i < adapters.adapter_count; ++i) {
608         // The first query should just check if the field exists and how big it is
609         LoaderQueryRegistryInfo filename_info = {
610             .query_type = LOADER_QUERY_REGISTRY_ADAPTER_KEY,
611             .query_flags =
612                 {
613                     .translate_path = true,
614                 },
615             .value_type = REG_MULTI_SZ,
616             .physical_adapter_index = 0,
617         };
618         wcsncpy(filename_info.value_name, value_name, sizeof(filename_info.value_name) / sizeof(WCHAR));
619         LoaderQueryAdapterInfo query_info;
620         query_info.handle = adapters.adapters[i].handle;
621         query_info.type = LOADER_QUERY_TYPE_REGISTRY;
622         query_info.private_data = &filename_info;
623         query_info.private_data_size = sizeof(filename_info);
624         status = fpLoaderQueryAdapterInfo(&query_info);
625 
626         // This error indicates that the type didn't match, so we'll try a REG_SZ
627         if (status != STATUS_SUCCESS) {
628             filename_info.value_type = REG_SZ;
629             status = fpLoaderQueryAdapterInfo(&query_info);
630         }
631 
632         if (status != STATUS_SUCCESS || filename_info.status != LOADER_QUERY_REGISTRY_STATUS_BUFFER_OVERFLOW) {
633             continue;
634         }
635 
636         while (status == STATUS_SUCCESS &&
637                ((LoaderQueryRegistryInfo *)query_info.private_data)->status == LOADER_QUERY_REGISTRY_STATUS_BUFFER_OVERFLOW) {
638             bool needs_copy = (full_info == NULL);
639             size_t full_size = sizeof(LoaderQueryRegistryInfo) + filename_info.output_value_size;
640             void *buffer =
641                 loader_instance_heap_realloc(inst, full_info, full_info_size, full_size, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
642             if (buffer == NULL) {
643                 result = VK_ERROR_OUT_OF_HOST_MEMORY;
644                 goto out;
645             }
646             full_info = buffer;
647             full_info_size = full_size;
648 
649             if (needs_copy) {
650                 memcpy(full_info, &filename_info, sizeof(LoaderQueryRegistryInfo));
651             }
652             query_info.private_data = full_info;
653             query_info.private_data_size = (UINT)full_info_size;
654             status = fpLoaderQueryAdapterInfo(&query_info);
655         }
656 
657         if (status != STATUS_SUCCESS || full_info->status != LOADER_QUERY_REGISTRY_STATUS_SUCCESS) {
658             goto out;
659         }
660 
661         // Convert the wide string to a narrow string
662         void *buffer = loader_instance_heap_realloc(inst, json_path, json_path_size, full_info->output_value_size,
663                                                     VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
664         if (buffer == NULL) {
665             result = VK_ERROR_OUT_OF_HOST_MEMORY;
666             goto out;
667         }
668         json_path = buffer;
669         json_path_size = full_info->output_value_size;
670 
671         // Iterate over each component string
672         for (const wchar_t *curr_path = full_info->output_string; curr_path[0] != '\0'; curr_path += wcslen(curr_path) + 1) {
673             WideCharToMultiByte(CP_UTF8, 0, curr_path, -1, json_path, (int)json_path_size, NULL, NULL);
674 
675             // Add the string to the output list
676             result = VK_SUCCESS;
677             windows_add_json_entry(inst, reg_data, reg_data_size, (LPCTSTR)L"EnumAdapters", REG_SZ, json_path,
678                                    (DWORD)strlen(json_path) + 1, &result);
679             if (result != VK_SUCCESS) {
680                 goto out;
681             }
682 
683             // If this is a string and not a multi-string, we don't want to go throught the loop more than once
684             if (full_info->value_type == REG_SZ) {
685                 break;
686             }
687         }
688     }
689 
690 out:
691     loader_instance_heap_free(inst, json_path);
692     loader_instance_heap_free(inst, full_info);
693     loader_instance_heap_free(inst, adapters.adapters);
694 
695     return result;
696 }
697 
698 // 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_data_files * out_files)699 VkResult windows_read_data_files_in_registry(const struct loader_instance *inst, enum loader_data_files_type data_file_type,
700                                              bool warn_if_not_present, char *registry_location,
701                                              struct loader_data_files *out_files) {
702     VkResult vk_result = VK_SUCCESS;
703     char *search_path = NULL;
704     uint32_t log_target_flag = 0;
705 
706     if (data_file_type == LOADER_DATA_FILE_MANIFEST_DRIVER) {
707         log_target_flag = VULKAN_LOADER_DRIVER_BIT;
708         loader_log(inst, log_target_flag, 0, "Checking for Driver Manifest files in Registry at %s", registry_location);
709     } else {
710         log_target_flag = VULKAN_LOADER_LAYER_BIT;
711         loader_log(inst, log_target_flag, 0, "Checking for Layer Manifest files in Registry at %s", registry_location);
712     }
713 
714     // These calls look at the PNP/Device section of the registry.
715     VkResult regHKR_result = VK_SUCCESS;
716     DWORD reg_size = 4096;
717     if (!strncmp(registry_location, VK_DRIVERS_INFO_REGISTRY_LOC, sizeof(VK_DRIVERS_INFO_REGISTRY_LOC))) {
718         // If we're looking for drivers we need to try enumerating adapters
719         regHKR_result = windows_read_manifest_from_d3d_adapters(inst, &search_path, &reg_size, LoaderPnpDriverRegistryWide());
720         if (regHKR_result == VK_INCOMPLETE) {
721             regHKR_result =
722                 windows_get_device_registry_files(inst, log_target_flag, &search_path, &reg_size, LoaderPnpDriverRegistry());
723         }
724     } else if (!strncmp(registry_location, VK_ELAYERS_INFO_REGISTRY_LOC, sizeof(VK_ELAYERS_INFO_REGISTRY_LOC))) {
725         regHKR_result = windows_read_manifest_from_d3d_adapters(inst, &search_path, &reg_size, LoaderPnpELayerRegistryWide());
726         if (regHKR_result == VK_INCOMPLETE) {
727             regHKR_result =
728                 windows_get_device_registry_files(inst, log_target_flag, &search_path, &reg_size, LoaderPnpELayerRegistry());
729         }
730     } else if (!strncmp(registry_location, VK_ILAYERS_INFO_REGISTRY_LOC, sizeof(VK_ILAYERS_INFO_REGISTRY_LOC))) {
731         regHKR_result = windows_read_manifest_from_d3d_adapters(inst, &search_path, &reg_size, LoaderPnpILayerRegistryWide());
732         if (regHKR_result == VK_INCOMPLETE) {
733             regHKR_result =
734                 windows_get_device_registry_files(inst, log_target_flag, &search_path, &reg_size, LoaderPnpILayerRegistry());
735         }
736     }
737 
738     if (regHKR_result == VK_ERROR_OUT_OF_HOST_MEMORY) {
739         vk_result = VK_ERROR_OUT_OF_HOST_MEMORY;
740         goto out;
741     }
742 
743     // This call looks into the Khronos non-device specific section of the registry for layer files.
744     bool use_secondary_hive = (data_file_type != LOADER_DATA_FILE_MANIFEST_DRIVER) && (!is_high_integrity());
745     VkResult reg_result = windows_get_registry_files(inst, registry_location, use_secondary_hive, &search_path, &reg_size);
746     if (reg_result == VK_ERROR_OUT_OF_HOST_MEMORY) {
747         vk_result = VK_ERROR_OUT_OF_HOST_MEMORY;
748         goto out;
749     }
750 
751     if ((VK_SUCCESS != reg_result && VK_SUCCESS != regHKR_result) || NULL == search_path) {
752         if (data_file_type == LOADER_DATA_FILE_MANIFEST_DRIVER) {
753             loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0,
754                        "windows_read_data_files_in_registry: Registry lookup failed to get ICD manifest files.  Possibly missing "
755                        "Vulkan driver?");
756             vk_result = VK_ERROR_INCOMPATIBLE_DRIVER;
757         } else {
758             if (warn_if_not_present) {
759                 if (data_file_type == LOADER_DATA_FILE_MANIFEST_IMPLICIT_LAYER ||
760                     data_file_type == LOADER_DATA_FILE_MANIFEST_EXPLICIT_LAYER) {
761                     // This is only a warning for layers
762                     loader_log(inst, VULKAN_LOADER_WARN_BIT | log_target_flag, 0,
763                                "windows_read_data_files_in_registry: Registry lookup failed to get layer manifest files.");
764                 } else {
765                     // This is only a warning for general data files
766                     loader_log(inst, VULKAN_LOADER_WARN_BIT | log_target_flag, 0,
767                                "windows_read_data_files_in_registry: Registry lookup failed to get data files.");
768                 }
769             }
770             // Return success for now since it's not critical for layers
771             vk_result = VK_SUCCESS;
772         }
773         goto out;
774     }
775 
776     // Now, parse the paths and add any manifest files found in them.
777     vk_result = add_data_files(inst, search_path, out_files, false);
778 
779 out:
780 
781     loader_instance_heap_free(inst, search_path);
782 
783     return vk_result;
784 }
785 
786 // This function allocates an array in sorted_devices which must be freed by the caller if not null
windows_read_sorted_physical_devices(struct loader_instance * inst,uint32_t * sorted_devices_count,struct loader_phys_dev_per_icd ** sorted_devices)787 VkResult windows_read_sorted_physical_devices(struct loader_instance *inst, uint32_t *sorted_devices_count,
788                                               struct loader_phys_dev_per_icd **sorted_devices) {
789     VkResult res = VK_SUCCESS;
790 
791     uint32_t sorted_alloc = 0;
792     struct loader_icd_term *icd_term = NULL;
793     IDXGIFactory6 *dxgi_factory = NULL;
794     HRESULT hres = fpCreateDXGIFactory1(&IID_IDXGIFactory6, (void **)&dxgi_factory);
795     if (hres != S_OK) {
796         loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Failed to create DXGI factory 6. Physical devices will not be sorted");
797         goto out;
798     }
799     sorted_alloc = 16;
800     *sorted_devices = loader_instance_heap_calloc(inst, sorted_alloc * sizeof(struct loader_phys_dev_per_icd),
801                                                   VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
802     if (*sorted_devices == NULL) {
803         res = VK_ERROR_OUT_OF_HOST_MEMORY;
804         goto out;
805     }
806 
807     for (uint32_t i = 0;; ++i) {
808         IDXGIAdapter1 *adapter;
809         hres = dxgi_factory->lpVtbl->EnumAdapterByGpuPreference(dxgi_factory, i, DXGI_GPU_PREFERENCE_UNSPECIFIED,
810                                                                 &IID_IDXGIAdapter1, (void **)&adapter);
811         if (hres == DXGI_ERROR_NOT_FOUND) {
812             break;  // No more adapters
813         } else if (hres != S_OK) {
814             loader_log(inst, VULKAN_LOADER_WARN_BIT, 0,
815                        "Failed to enumerate adapters by GPU preference at index %u. This adapter will not be sorted", i);
816             break;
817         }
818 
819         DXGI_ADAPTER_DESC1 description;
820         hres = adapter->lpVtbl->GetDesc1(adapter, &description);
821         if (hres != S_OK) {
822             loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Failed to get adapter LUID index %u. This adapter will not be sorted", i);
823             continue;
824         }
825 
826         if (sorted_alloc <= i) {
827             uint32_t old_size = sorted_alloc * sizeof(struct loader_phys_dev_per_icd);
828             *sorted_devices =
829                 loader_instance_heap_realloc(inst, *sorted_devices, old_size, 2 * old_size, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
830             if (*sorted_devices == NULL) {
831                 adapter->lpVtbl->Release(adapter);
832                 res = VK_ERROR_OUT_OF_HOST_MEMORY;
833                 goto out;
834             }
835             sorted_alloc *= 2;
836         }
837         struct loader_phys_dev_per_icd *sorted_array = *sorted_devices;
838         sorted_array[*sorted_devices_count].device_count = 0;
839         sorted_array[*sorted_devices_count].physical_devices = NULL;
840 
841         icd_term = inst->icd_terms;
842         for (uint32_t icd_idx = 0; NULL != icd_term; icd_term = icd_term->next, icd_idx++) {
843             // This is the new behavior, which cannot be run unless the ICD provides EnumerateAdapterPhysicalDevices
844             if (icd_term->scanned_icd->EnumerateAdapterPhysicalDevices == NULL) {
845                 continue;
846             }
847 
848             uint32_t count = 0;
849             VkResult vkres =
850                 icd_term->scanned_icd->EnumerateAdapterPhysicalDevices(icd_term->instance, description.AdapterLuid, &count, NULL);
851             if (vkres == VK_ERROR_INCOMPATIBLE_DRIVER) {
852                 continue;  // This driver doesn't support the adapter
853             } else if (vkres == VK_ERROR_OUT_OF_HOST_MEMORY) {
854                 res = VK_ERROR_OUT_OF_HOST_MEMORY;
855                 goto out;
856             } else if (vkres != VK_SUCCESS) {
857                 loader_log(inst, VULKAN_LOADER_WARN_BIT, 0,
858                            "Failed to convert DXGI adapter into Vulkan physical device with unexpected error code");
859                 continue;
860             }
861 
862             // Get the actual physical devices
863             if (0 != count) {
864                 do {
865                     sorted_array[*sorted_devices_count].physical_devices =
866                         loader_instance_heap_realloc(inst, sorted_array[*sorted_devices_count].physical_devices,
867                                                      sorted_array[*sorted_devices_count].device_count * sizeof(VkPhysicalDevice),
868                                                      count * sizeof(VkPhysicalDevice), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
869                     if (sorted_array[*sorted_devices_count].physical_devices == NULL) {
870                         res = VK_ERROR_OUT_OF_HOST_MEMORY;
871                         break;
872                     }
873                     sorted_array[*sorted_devices_count].device_count = count;
874                 } while ((vkres = icd_term->scanned_icd->EnumerateAdapterPhysicalDevices(
875                               icd_term->instance, description.AdapterLuid, &count,
876                               sorted_array[*sorted_devices_count].physical_devices)) == VK_INCOMPLETE);
877             }
878 
879             if (vkres != VK_SUCCESS) {
880                 loader_instance_heap_free(inst, sorted_array[*sorted_devices_count].physical_devices);
881                 sorted_array[*sorted_devices_count].physical_devices = NULL;
882                 if (vkres == VK_ERROR_OUT_OF_HOST_MEMORY) {
883                     res = VK_ERROR_OUT_OF_HOST_MEMORY;
884                     goto out;
885                 } else {
886                     loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Failed to convert DXGI adapter into Vulkan physical device");
887                     continue;
888                 }
889             }
890             sorted_array[*sorted_devices_count].device_count = count;
891             sorted_array[*sorted_devices_count].icd_index = icd_idx;
892             sorted_array[*sorted_devices_count].icd_term = icd_term;
893             (*sorted_devices_count)++;
894         }
895 
896         adapter->lpVtbl->Release(adapter);
897     }
898 
899     dxgi_factory->lpVtbl->Release(dxgi_factory);
900 
901 out:
902     if (*sorted_devices_count == 0 && *sorted_devices != NULL) {
903         loader_instance_heap_free(inst, *sorted_devices);
904         *sorted_devices = NULL;
905     }
906     *sorted_devices_count = *sorted_devices_count;
907     *sorted_devices = *sorted_devices;
908     return res;
909 }
910 
windows_initialize_dxgi(void)911 VkLoaderFeatureFlags windows_initialize_dxgi(void) {
912     VkLoaderFeatureFlags feature_flags = 0;
913     IDXGIFactory6 *dxgi_factory = NULL;
914     HRESULT hres = fpCreateDXGIFactory1(&IID_IDXGIFactory6, (void **)&dxgi_factory);
915     if (hres == S_OK) {
916         feature_flags |= VK_LOADER_FEATURE_PHYSICAL_DEVICE_SORTING;
917         dxgi_factory->lpVtbl->Release(dxgi_factory);
918     }
919     return feature_flags;
920 }
921 
922 // Sort the VkPhysicalDevices that are part of the current group with the list passed in from the sorted list.
923 // Multiple groups could have devices out of the same sorted list, however, a single group's devices must all come
924 // from the same sorted list.
windows_sort_devices_in_group(struct loader_instance * inst,struct VkPhysicalDeviceGroupProperties * group_props,struct loader_phys_dev_per_icd * icd_sorted_list)925 void windows_sort_devices_in_group(struct loader_instance *inst, struct VkPhysicalDeviceGroupProperties *group_props,
926                                    struct loader_phys_dev_per_icd *icd_sorted_list) {
927     uint32_t cur_index = 0;
928     for (uint32_t dev = 0; dev < icd_sorted_list->device_count; ++dev) {
929         for (uint32_t grp_dev = cur_index; grp_dev < group_props->physicalDeviceCount; ++grp_dev) {
930             if (icd_sorted_list->physical_devices[dev] == group_props->physicalDevices[grp_dev]) {
931                 if (cur_index != grp_dev) {
932                     VkPhysicalDevice swap_dev = group_props->physicalDevices[cur_index];
933                     group_props->physicalDevices[cur_index] = group_props->physicalDevices[grp_dev];
934                     group_props->physicalDevices[grp_dev] = swap_dev;
935                 }
936                 cur_index++;
937                 break;
938             }
939         }
940     }
941     if (cur_index == 0) {
942         loader_log(inst, VULKAN_LOADER_WARN_BIT, 0,
943                    "windows_sort_devices_in_group:  Never encountered a device in the sorted list group");
944     }
945 }
946 
947 // 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_phys_dev_per_icd * sorted_phys_dev_array)948 VkResult windows_sort_physical_device_groups(struct loader_instance *inst, const uint32_t group_count,
949                                              struct loader_physical_device_group_term *sorted_group_term,
950                                              const uint32_t sorted_device_count,
951                                              struct loader_phys_dev_per_icd *sorted_phys_dev_array) {
952     if (0 == group_count || NULL == sorted_group_term) {
953         loader_log(inst, VULKAN_LOADER_WARN_BIT, 0,
954                    "windows_sort_physical_device_groups: Called with invalid information (Group count %d, Sorted Info %p)",
955                    group_count, sorted_group_term);
956         return VK_ERROR_INITIALIZATION_FAILED;
957     }
958 
959     uint32_t new_index = 0;
960     for (uint32_t icd = 0; icd < sorted_device_count; ++icd) {
961         for (uint32_t dev = 0; dev < sorted_phys_dev_array[icd].device_count; ++dev) {
962             // Find a group associated with a given device
963             for (uint32_t group = new_index; group < group_count; ++group) {
964                 bool device_found = false;
965                 // Look for the current sorted device in a group and put it in the correct location if it isn't already
966                 for (uint32_t grp_dev = 0; grp_dev < sorted_group_term[group].group_props.physicalDeviceCount; ++grp_dev) {
967                     if (sorted_group_term[group].group_props.physicalDevices[grp_dev] ==
968                         sorted_phys_dev_array[icd].physical_devices[dev]) {
969                         // First, sort devices inside of group to be in priority order
970                         windows_sort_devices_in_group(inst, &sorted_group_term[group].group_props, &sorted_phys_dev_array[icd]);
971 
972                         // Second, move the group up in priority if it needs to be
973                         if (new_index != group) {
974                             struct loader_physical_device_group_term tmp = sorted_group_term[new_index];
975                             sorted_group_term[new_index] = sorted_group_term[group];
976                             sorted_group_term[group] = tmp;
977                         }
978                         device_found = true;
979                         new_index++;
980                         break;
981                     }
982                 }
983                 if (device_found) {
984                     break;
985                 }
986             }
987         }
988     }
989     return VK_SUCCESS;
990 }
991 
windows_get_app_package_manifest_path(const struct loader_instance * inst)992 char *windows_get_app_package_manifest_path(const struct loader_instance *inst) {
993     // These functions are only available on Windows 8 and above, load them dynamically for compatibility with Windows 7
994     typedef LONG(WINAPI * PFN_GetPackagesByPackageFamily)(PCWSTR, UINT32 *, PWSTR *, UINT32 *, WCHAR *);
995     PFN_GetPackagesByPackageFamily fpGetPackagesByPackageFamily =
996         (PFN_GetPackagesByPackageFamily)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetPackagesByPackageFamily");
997     if (!fpGetPackagesByPackageFamily) {
998         return NULL;
999     }
1000     typedef LONG(WINAPI * PFN_GetPackagePathByFullName)(PCWSTR, UINT32 *, PWSTR);
1001     PFN_GetPackagePathByFullName fpGetPackagePathByFullName =
1002         (PFN_GetPackagePathByFullName)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetPackagePathByFullName");
1003     if (!fpGetPackagePathByFullName) {
1004         return NULL;
1005     }
1006 
1007     UINT32 numPackages = 0, bufferLength = 0;
1008     /* This literal string identifies the Microsoft-published OpenCL and OpenGL Compatibility Pack
1009      * (so named at the time this is being added), which contains OpenGLOn12 and OpenCLOn12 mapping
1010      * layers, and will contain VulkanOn12 (aka Dozen) going forward.
1011      */
1012     PCWSTR familyName = L"Microsoft.D3DMappingLayers_8wekyb3d8bbwe";
1013     if (ERROR_INSUFFICIENT_BUFFER != fpGetPackagesByPackageFamily(familyName, &numPackages, NULL, &bufferLength, NULL) ||
1014         numPackages == 0 || bufferLength == 0) {
1015         loader_log(inst, VULKAN_LOADER_INFO_BIT, 0,
1016                    "windows_get_app_package_manifest_path: Failed to find mapping layers packages by family name\n");
1017         return NULL;
1018     }
1019 
1020     char *ret = NULL;
1021     WCHAR *buffer = loader_instance_heap_alloc(inst, sizeof(WCHAR) * bufferLength, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
1022     PWSTR *packages = loader_instance_heap_alloc(inst, sizeof(PWSTR) * numPackages, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
1023     if (!buffer || !packages) {
1024         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
1025                    "windows_get_app_package_manifest_path: Failed to allocate memory for package names\n");
1026         goto cleanup;
1027     }
1028 
1029     if (ERROR_SUCCESS != fpGetPackagesByPackageFamily(familyName, &numPackages, packages, &bufferLength, buffer)) {
1030         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
1031                    "windows_get_app_package_manifest_path: Failed to mapping layers package full names\n");
1032         goto cleanup;
1033     }
1034 
1035     UINT32 pathLength = 0;
1036     WCHAR path[MAX_PATH];
1037     memset(path, 0, sizeof(path));
1038     if (ERROR_INSUFFICIENT_BUFFER != fpGetPackagePathByFullName(packages[0], &pathLength, NULL) || pathLength > MAX_PATH ||
1039         ERROR_SUCCESS != fpGetPackagePathByFullName(packages[0], &pathLength, path)) {
1040         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
1041                    "windows_get_app_package_manifest_path: Failed to get mapping layers package path\n");
1042         goto cleanup;
1043     }
1044 
1045     int narrowPathLength = WideCharToMultiByte(CP_ACP, 0, path, -1, NULL, 0, NULL, NULL);
1046     if (narrowPathLength == 0) {
1047         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
1048                    "windows_get_app_package_manifest_path: Failed to convert path from wide to narrow\n");
1049         goto cleanup;
1050     }
1051 
1052     ret = loader_instance_heap_alloc(inst, narrowPathLength, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
1053     if (!ret) {
1054         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "windows_get_app_package_manifest_path: Failed to allocate path\n");
1055         goto cleanup;
1056     }
1057 
1058     narrowPathLength = WideCharToMultiByte(CP_ACP, 0, path, -1, ret, narrowPathLength, NULL, NULL);
1059     assert((size_t)narrowPathLength == strlen(ret) + 1);
1060 
1061 cleanup:
1062     loader_instance_heap_free(inst, buffer);
1063     loader_instance_heap_free(inst, packages);
1064     return ret;
1065 }
1066 #endif  // _WIN32
1067