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 63typedef HRESULT(APIENTRY *PFN_CreateDXGIFactory1)(REFIID riid, void **ppFactory); 64PFN_CreateDXGIFactory1 fpCreateDXGIFactory1; 65 66// Empty function just so windows_initialization can find the current module location 67void function_for_finding_the_current_module(void) {} 68 69void 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 98BOOL 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 116bool 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 169bool 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 227out: 228 loader_instance_heap_free(inst, manifest_path); 229 RegCloseKey(hkrKey); 230 return found; 231} 232 233VkResult 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 352VkResult 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 566out: 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 575VkResult 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 698out: 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. 707VkResult 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 789out: 790 791 loader_instance_heap_free(inst, search_path); 792 793 return vk_result; 794} 795 796VkResult 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. 872void 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 926VkResult 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 1001out: 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 1009VkLoaderFeatureFlags 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. 1023void 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 1046VkResult 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 1090char *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 1159cleanup: 1160 loader_instance_heap_free(inst, buffer); 1161 loader_instance_heap_free(inst, packages); 1162 return ret; 1163} 1164 1165VkResult 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 1211VkResult 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 1251out: 1252 if (NULL != key) { 1253 RegCloseKey(key); 1254 } 1255 1256 return result; 1257} 1258 1259#endif // _WIN32 1260