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