1/* 2 * Copyright (c) 2021-2023 The Khronos Group Inc. 3 * Copyright (c) 2021-2023 Valve Corporation 4 * Copyright (c) 2021-2023 LunarG, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and/or associated documentation files (the "Materials"), to 8 * deal in the Materials without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Materials, and to permit persons to whom the Materials are 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice(s) and this permission notice shall be included in 14 * all copies or substantial portions of the Materials. 15 * 16 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 * 20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE 23 * USE OR OTHER DEALINGS IN THE MATERIALS. 24 * 25 * Author: Charles Giessen <charles@lunarg.com> 26 */ 27 28#include "test_environment.h" 29 30#include <mutex> 31 32struct MemoryTrackerSettings { 33 bool should_fail_on_allocation = false; 34 size_t fail_after_allocations = 0; // fail after this number of allocations in total 35 bool should_fail_after_set_number_of_calls = false; 36 size_t fail_after_calls = 0; // fail after this number of calls to alloc or realloc 37}; 38 39class MemoryTracker { 40 std::mutex main_mutex; 41 MemoryTrackerSettings settings{}; 42 VkAllocationCallbacks callbacks{}; 43 // Implementation internals 44 struct AllocationDetails { 45 std::unique_ptr<char[]> allocation; 46 size_t requested_size_bytes; 47 size_t actual_size_bytes; 48 size_t alignment; 49 VkSystemAllocationScope alloc_scope; 50 }; 51 const static size_t UNKNOWN_ALLOCATION = std::numeric_limits<size_t>::max(); 52 size_t allocation_count = 0; 53 size_t call_count = 0; 54 std::unordered_map<void*, AllocationDetails> allocations; 55 56 void* allocate(size_t size, size_t alignment, VkSystemAllocationScope alloc_scope) { 57 if ((settings.should_fail_on_allocation && allocation_count == settings.fail_after_allocations) || 58 (settings.should_fail_after_set_number_of_calls && call_count == settings.fail_after_calls)) { 59 return nullptr; 60 } 61 call_count++; 62 allocation_count++; 63 AllocationDetails detail{nullptr, size, size + (alignment - 1), alignment, alloc_scope}; 64 detail.allocation = std::unique_ptr<char[]>(new char[detail.actual_size_bytes]); 65 if (!detail.allocation) { 66 abort(); 67 }; 68 uint64_t addr = (uint64_t)detail.allocation.get(); 69 addr += (alignment - 1); 70 addr &= ~(alignment - 1); 71 void* aligned_alloc = (void*)addr; 72 allocations.insert(std::make_pair(aligned_alloc, std::move(detail))); 73 return aligned_alloc; 74 } 75 void* reallocate(void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope alloc_scope) { 76 if (pOriginal == nullptr) { 77 return allocate(size, alignment, alloc_scope); 78 } 79 auto elem = allocations.find(pOriginal); 80 if (elem == allocations.end()) return nullptr; 81 size_t original_size = elem->second.requested_size_bytes; 82 83 // We only care about the case where realloc is used to increase the size 84 if (size >= original_size && settings.should_fail_after_set_number_of_calls && call_count == settings.fail_after_calls) 85 return nullptr; 86 call_count++; 87 if (size == 0) { 88 allocations.erase(elem); 89 allocation_count--; 90 return nullptr; 91 } else if (size < original_size) { 92 return pOriginal; 93 } else { 94 void* new_alloc = allocate(size, alignment, alloc_scope); 95 if (new_alloc == nullptr) return nullptr; 96 allocation_count--; // allocate() increments this, we we don't want that 97 call_count--; // allocate() also increments this, we don't want that 98 memcpy(new_alloc, pOriginal, original_size); 99 allocations.erase(elem); 100 return new_alloc; 101 } 102 } 103 void free(void* pMemory) { 104 if (pMemory == nullptr) return; 105 auto elem = allocations.find(pMemory); 106 if (elem == allocations.end()) return; 107 allocations.erase(elem); 108 assert(allocation_count != 0 && "Cant free when there are no valid allocations"); 109 allocation_count--; 110 } 111 112 // Implementation of public functions 113 void* impl_allocation(size_t size, size_t alignment, VkSystemAllocationScope allocationScope) noexcept { 114 std::lock_guard<std::mutex> lg(main_mutex); 115 void* addr = allocate(size, alignment, allocationScope); 116 return addr; 117 } 118 void* impl_reallocation(void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) noexcept { 119 std::lock_guard<std::mutex> lg(main_mutex); 120 void* addr = reallocate(pOriginal, size, alignment, allocationScope); 121 return addr; 122 } 123 void impl_free(void* pMemory) noexcept { 124 std::lock_guard<std::mutex> lg(main_mutex); 125 free(pMemory); 126 } 127 void impl_internal_allocation_notification([[maybe_unused]] size_t size, 128 [[maybe_unused]] VkInternalAllocationType allocationType, 129 [[maybe_unused]] VkSystemAllocationScope allocationScope) noexcept { 130 std::lock_guard<std::mutex> lg(main_mutex); 131 // TODO? 132 } 133 void impl_internal_free([[maybe_unused]] size_t size, [[maybe_unused]] VkInternalAllocationType allocationType, 134 [[maybe_unused]] VkSystemAllocationScope allocationScope) noexcept { 135 std::lock_guard<std::mutex> lg(main_mutex); 136 // TODO? 137 } 138 139 public: 140 MemoryTracker(MemoryTrackerSettings settings) noexcept : settings(settings) { 141 allocations.reserve(3000); 142 143 callbacks.pUserData = this; 144 callbacks.pfnAllocation = public_allocation; 145 callbacks.pfnReallocation = public_reallocation; 146 callbacks.pfnFree = public_free; 147 callbacks.pfnInternalAllocation = public_internal_allocation_notification; 148 callbacks.pfnInternalFree = public_internal_free; 149 } 150 MemoryTracker() noexcept : MemoryTracker(MemoryTrackerSettings{}) {} 151 152 VkAllocationCallbacks* get() noexcept { return &callbacks; } 153 154 bool empty() noexcept { return allocation_count == 0; } 155 156 // Static callbacks 157 static VKAPI_ATTR void* VKAPI_CALL public_allocation(void* pUserData, size_t size, size_t alignment, 158 VkSystemAllocationScope allocationScope) noexcept { 159 return reinterpret_cast<MemoryTracker*>(pUserData)->impl_allocation(size, alignment, allocationScope); 160 } 161 static VKAPI_ATTR void* VKAPI_CALL public_reallocation(void* pUserData, void* pOriginal, size_t size, size_t alignment, 162 VkSystemAllocationScope allocationScope) noexcept { 163 return reinterpret_cast<MemoryTracker*>(pUserData)->impl_reallocation(pOriginal, size, alignment, allocationScope); 164 } 165 static VKAPI_ATTR void VKAPI_CALL public_free(void* pUserData, void* pMemory) noexcept { 166 reinterpret_cast<MemoryTracker*>(pUserData)->impl_free(pMemory); 167 } 168 static VKAPI_ATTR void VKAPI_CALL public_internal_allocation_notification(void* pUserData, size_t size, 169 VkInternalAllocationType allocationType, 170 VkSystemAllocationScope allocationScope) noexcept { 171 reinterpret_cast<MemoryTracker*>(pUserData)->impl_internal_allocation_notification(size, allocationType, allocationScope); 172 } 173 static VKAPI_ATTR void VKAPI_CALL public_internal_free(void* pUserData, size_t size, VkInternalAllocationType allocationType, 174 VkSystemAllocationScope allocationScope) noexcept { 175 reinterpret_cast<MemoryTracker*>(pUserData)->impl_internal_free(size, allocationType, allocationScope); 176 } 177}; 178 179// Test making sure the allocation functions are called to allocate and cleanup everything during 180// a CreateInstance/DestroyInstance call pair. 181TEST(Allocation, Instance) { 182 FrameworkEnvironment env{}; 183 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); 184 185 MemoryTracker tracker; 186 { 187 InstWrapper inst{env.vulkan_functions, tracker.get()}; 188 ASSERT_NO_FATAL_FAILURE(inst.CheckCreate()); 189 } 190 ASSERT_TRUE(tracker.empty()); 191} 192 193// Test making sure the allocation functions are called to allocate and cleanup everything during 194// a CreateInstance/DestroyInstance call pair with a call to GetInstanceProcAddr. 195TEST(Allocation, GetInstanceProcAddr) { 196 FrameworkEnvironment env{}; 197 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); 198 199 MemoryTracker tracker; 200 { 201 InstWrapper inst{env.vulkan_functions, tracker.get()}; 202 ASSERT_NO_FATAL_FAILURE(inst.CheckCreate()); 203 204 auto* pfnCreateDevice = inst->vkGetInstanceProcAddr(inst, "vkCreateDevice"); 205 auto* pfnDestroyDevice = inst->vkGetInstanceProcAddr(inst, "vkDestroyDevice"); 206 ASSERT_TRUE(pfnCreateDevice != nullptr && pfnDestroyDevice != nullptr); 207 } 208 ASSERT_TRUE(tracker.empty()); 209} 210 211// Test making sure the allocation functions are called to allocate and cleanup everything during 212// a vkEnumeratePhysicalDevices call pair. 213TEST(Allocation, EnumeratePhysicalDevices) { 214 FrameworkEnvironment env{}; 215 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device("physical_device_0"); 216 217 MemoryTracker tracker; 218 { 219 InstWrapper inst{env.vulkan_functions, tracker.get()}; 220 ASSERT_NO_FATAL_FAILURE(inst.CheckCreate()); 221 uint32_t physical_count = 1; 222 uint32_t returned_physical_count = 0; 223 ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); 224 ASSERT_EQ(physical_count, returned_physical_count); 225 226 VkPhysicalDevice physical_device; 227 ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, &physical_device)); 228 ASSERT_EQ(physical_count, returned_physical_count); 229 } 230 ASSERT_TRUE(tracker.empty()); 231} 232 233// Test making sure the allocation functions are called to allocate and cleanup everything from 234// vkCreateInstance, to vkCreateDevicce, and then through their destructors. With special 235// allocators used on both the instance and device. 236TEST(Allocation, InstanceAndDevice) { 237 FrameworkEnvironment env{}; 238 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) 239 .add_physical_device(PhysicalDevice{"physical_device_0"} 240 .add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, false}) 241 .finish()); 242 243 MemoryTracker tracker; 244 { 245 InstWrapper inst{env.vulkan_functions, tracker.get()}; 246 ASSERT_NO_FATAL_FAILURE(inst.CheckCreate()); 247 248 uint32_t physical_count = 1; 249 uint32_t returned_physical_count = 0; 250 ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); 251 ASSERT_EQ(physical_count, returned_physical_count); 252 253 VkPhysicalDevice physical_device; 254 ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, &physical_device)); 255 ASSERT_EQ(physical_count, returned_physical_count); 256 257 uint32_t family_count = 1; 258 uint32_t returned_family_count = 0; 259 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, nullptr); 260 ASSERT_EQ(returned_family_count, family_count); 261 262 VkQueueFamilyProperties family; 263 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, &family); 264 ASSERT_EQ(returned_family_count, family_count); 265 ASSERT_EQ(family.queueFlags, static_cast<VkQueueFlags>(VK_QUEUE_GRAPHICS_BIT)); 266 ASSERT_EQ(family.queueCount, family_count); 267 ASSERT_EQ(family.timestampValidBits, 0U); 268 269 DeviceCreateInfo dev_create_info; 270 dev_create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f)); 271 272 VkDevice device; 273 ASSERT_EQ(inst->vkCreateDevice(physical_device, dev_create_info.get(), tracker.get(), &device), VK_SUCCESS); 274 275 VkQueue queue; 276 inst->vkGetDeviceQueue(device, 0, 0, &queue); 277 278 inst->vkDestroyDevice(device, tracker.get()); 279 } 280 ASSERT_TRUE(tracker.empty()); 281} 282// Test making sure the allocation functions are called to allocate and cleanup everything from 283// vkCreateInstance, to vkCreateDevicce, and then through their destructors. With special 284// allocators used on only the instance and not the device. 285TEST(Allocation, InstanceButNotDevice) { 286 FrameworkEnvironment env{}; 287 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) 288 .add_physical_device(PhysicalDevice{"physical_device_0"} 289 .add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, false}) 290 .finish()); 291 292 MemoryTracker tracker; 293 { 294 InstWrapper inst{env.vulkan_functions, tracker.get()}; 295 ASSERT_NO_FATAL_FAILURE(inst.CheckCreate()); 296 297 uint32_t physical_count = 1; 298 uint32_t returned_physical_count = 0; 299 ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); 300 ASSERT_EQ(physical_count, returned_physical_count); 301 302 VkPhysicalDevice physical_device; 303 ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, &physical_device)); 304 ASSERT_EQ(physical_count, returned_physical_count); 305 306 uint32_t family_count = 1; 307 uint32_t returned_family_count = 0; 308 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, nullptr); 309 ASSERT_EQ(returned_family_count, family_count); 310 311 VkQueueFamilyProperties family; 312 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, &family); 313 ASSERT_EQ(returned_family_count, family_count); 314 ASSERT_EQ(family.queueFlags, static_cast<VkQueueFlags>(VK_QUEUE_GRAPHICS_BIT)); 315 ASSERT_EQ(family.queueCount, family_count); 316 ASSERT_EQ(family.timestampValidBits, 0U); 317 318 DeviceCreateInfo dev_create_info; 319 dev_create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f)); 320 321 VkDevice device; 322 ASSERT_EQ(inst->vkCreateDevice(physical_device, dev_create_info.get(), nullptr, &device), VK_SUCCESS); 323 VkQueue queue; 324 inst->vkGetDeviceQueue(device, 0, 0, &queue); 325 326 inst->vkDestroyDevice(device, nullptr); 327 } 328 ASSERT_TRUE(tracker.empty()); 329} 330 331// Test making sure the allocation functions are called to allocate and cleanup everything from 332// vkCreateInstance, to vkCreateDevicce, and then through their destructors. With special 333// allocators used on only the device and not the instance. 334TEST(Allocation, DeviceButNotInstance) { 335 FrameworkEnvironment env{}; 336 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) 337 .add_physical_device(PhysicalDevice{"physical_device_0"} 338 .add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, false}) 339 .finish()); 340 341 const char* layer_name = "VK_LAYER_implicit"; 342 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 343 .set_name(layer_name) 344 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 345 .set_disable_environment("DISABLE_ENV")), 346 "test_layer.json"); 347 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 348 349 MemoryTracker tracker; 350 { 351 InstWrapper inst{env.vulkan_functions}; 352 ASSERT_NO_FATAL_FAILURE(inst.CheckCreate()); 353 354 uint32_t physical_count = 1; 355 uint32_t returned_physical_count = 0; 356 ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); 357 ASSERT_EQ(physical_count, returned_physical_count); 358 359 VkPhysicalDevice physical_device; 360 ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, &physical_device)); 361 ASSERT_EQ(physical_count, returned_physical_count); 362 363 uint32_t family_count = 1; 364 uint32_t returned_family_count = 0; 365 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, nullptr); 366 ASSERT_EQ(returned_family_count, family_count); 367 368 VkQueueFamilyProperties family; 369 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &returned_family_count, &family); 370 ASSERT_EQ(returned_family_count, family_count); 371 ASSERT_EQ(family.queueFlags, static_cast<VkQueueFlags>(VK_QUEUE_GRAPHICS_BIT)); 372 ASSERT_EQ(family.queueCount, family_count); 373 ASSERT_EQ(family.timestampValidBits, 0U); 374 375 DeviceCreateInfo dev_create_info; 376 dev_create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f)); 377 378 VkDevice device; 379 ASSERT_EQ(inst->vkCreateDevice(physical_device, dev_create_info.get(), tracker.get(), &device), VK_SUCCESS); 380 381 VkQueue queue; 382 inst->vkGetDeviceQueue(device, 0, 0, &queue); 383 384 inst->vkDestroyDevice(device, tracker.get()); 385 } 386 ASSERT_TRUE(tracker.empty()); 387} 388 389// Test failure during vkCreateInstance to make sure we don't leak memory if 390// one of the out-of-memory conditions trigger. 391TEST(Allocation, CreateInstanceIntentionalAllocFail) { 392 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 393 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); 394 395 const char* layer_name = "VK_LAYER_implicit"; 396 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 397 .set_name(layer_name) 398 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 399 .set_disable_environment("DISABLE_ENV")), 400 "test_layer.json"); 401 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 402 403 size_t fail_index = 0; 404 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 405 while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) { 406 MemoryTracker tracker({false, 0, true, fail_index}); 407 408 VkInstance instance; 409 InstanceCreateInfo inst_create_info{}; 410 result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance); 411 if (result == VK_SUCCESS) { 412 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 413 } 414 ASSERT_TRUE(tracker.empty()); 415 fail_index++; 416 } 417} 418 419// Test failure during vkCreateInstance to make sure we don't leak memory if 420// one of the out-of-memory conditions trigger and there are invalid jsons in the same folder 421TEST(Allocation, CreateInstanceIntentionalAllocFailInvalidManifests) { 422 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 423 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); 424 425 std::vector<std::string> invalid_jsons; 426 invalid_jsons.push_back(","); 427 invalid_jsons.push_back("{},[]"); 428 invalid_jsons.push_back("{ \"foo\":\"bar\", }"); 429 invalid_jsons.push_back("{\"foo\":\"bar\", \"baz\": [], },"); 430 invalid_jsons.push_back("{\"foo\":\"bar\", \"baz\": [{},] },"); 431 invalid_jsons.push_back("{\"foo\":\"bar\", \"baz\": {\"fee\"} },"); 432 invalid_jsons.push_back("{\"\":\"bar\", \"baz\": {}"); 433 invalid_jsons.push_back("{\"foo\":\"bar\", \"baz\": {\"fee\":1234, true, \"ab\":\"bc\"} },"); 434 435 for (size_t i = 0; i < invalid_jsons.size(); i++) { 436 auto file_name = std::string("invalid_implicit_layer_") + std::to_string(i) + ".json"; 437 fs::path new_path = env.get_folder(ManifestLocation::implicit_layer).write_manifest(file_name, invalid_jsons[i]); 438 env.platform_shim->add_manifest(ManifestCategory::implicit_layer, new_path); 439 } 440 441 const char* layer_name = "VkLayerImplicit0"; 442 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 443 .set_name(layer_name) 444 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 445 .set_disable_environment("DISABLE_ENV")), 446 "test_layer.json"); 447 448 size_t fail_index = 0; 449 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 450 while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) { 451 MemoryTracker tracker({false, 0, true, fail_index}); 452 453 VkInstance instance; 454 InstanceCreateInfo inst_create_info{}; 455 result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance); 456 if (result == VK_SUCCESS) { 457 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 458 } 459 ASSERT_TRUE(tracker.empty()); 460 fail_index++; 461 } 462} 463 464// Test failure during vkCreateInstance & surface creation to make sure we don't leak memory if 465// one of the out-of-memory conditions trigger. 466TEST(Allocation, CreateSurfaceIntentionalAllocFail) { 467 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 468 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).setup_WSI(); 469 470 const char* layer_name = "VK_LAYER_implicit"; 471 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 472 .set_name(layer_name) 473 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 474 .set_disable_environment("DISABLE_ENV")), 475 "test_layer.json"); 476 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 477 478 size_t fail_index = 0; 479 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 480 while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) { 481 MemoryTracker tracker({false, 0, true, fail_index}); 482 483 VkInstance instance; 484 InstanceCreateInfo inst_create_info{}; 485 inst_create_info.setup_WSI(); 486 result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance); 487 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 488 ASSERT_TRUE(tracker.empty()); 489 fail_index++; 490 continue; 491 } 492 493 VkSurfaceKHR surface{}; 494 result = create_surface(&env.vulkan_functions, instance, surface); 495 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 496 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 497 ASSERT_TRUE(tracker.empty()); 498 fail_index++; 499 continue; 500 } 501 env.vulkan_functions.vkDestroySurfaceKHR(instance, surface, tracker.get()); 502 503 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 504 ASSERT_TRUE(tracker.empty()); 505 fail_index++; 506 } 507} 508 509// Test failure during vkCreateInstance to make sure we don't leak memory if 510// one of the out-of-memory conditions trigger. 511TEST(Allocation, CreateInstanceIntentionalAllocFailWithSettingsFilePresent) { 512 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 513 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); 514 515 const char* layer_name = "VK_LAYER_implicit"; 516 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 517 .set_name(layer_name) 518 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 519 .set_disable_environment("DISABLE_ENV")), 520 "test_layer.json"); 521 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 522 523 env.update_loader_settings( 524 env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( 525 LoaderSettingsLayerConfiguration{} 526 .set_name(layer_name) 527 .set_control("auto") 528 .set_path(env.get_shimmed_layer_manifest_path(0).str())))); 529 530 size_t fail_index = 0; 531 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 532 while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) { 533 MemoryTracker tracker({false, 0, true, fail_index}); 534 535 VkInstance instance; 536 InstanceCreateInfo inst_create_info{}; 537 result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance); 538 if (result == VK_SUCCESS) { 539 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 540 } 541 ASSERT_TRUE(tracker.empty()); 542 fail_index++; 543 } 544} 545 546// Test failure during vkCreateInstance & surface creation to make sure we don't leak memory if 547// one of the out-of-memory conditions trigger. 548TEST(Allocation, CreateSurfaceIntentionalAllocFailWithSettingsFilePresent) { 549 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 550 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).setup_WSI(); 551 552 const char* layer_name = "VK_LAYER_implicit"; 553 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 554 .set_name(layer_name) 555 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 556 .set_disable_environment("DISABLE_ENV")), 557 "test_layer.json"); 558 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 559 env.update_loader_settings( 560 env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( 561 LoaderSettingsLayerConfiguration{} 562 .set_name(layer_name) 563 .set_control("auto") 564 .set_path(env.get_shimmed_layer_manifest_path(0).str())))); 565 566 size_t fail_index = 0; 567 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 568 while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) { 569 MemoryTracker tracker({false, 0, true, fail_index}); 570 571 VkInstance instance; 572 InstanceCreateInfo inst_create_info{}; 573 inst_create_info.setup_WSI(); 574 result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance); 575 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 576 ASSERT_TRUE(tracker.empty()); 577 fail_index++; 578 continue; 579 } 580 581 VkSurfaceKHR surface{}; 582 result = create_surface(&env.vulkan_functions, instance, surface); 583 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 584 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 585 ASSERT_TRUE(tracker.empty()); 586 fail_index++; 587 continue; 588 } 589 env.vulkan_functions.vkDestroySurfaceKHR(instance, surface, tracker.get()); 590 591 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 592 ASSERT_TRUE(tracker.empty()); 593 fail_index++; 594 } 595} 596 597// Test failure during vkCreateInstance to make sure we don't leak memory if 598// one of the out-of-memory conditions trigger. 599TEST(Allocation, DriverEnvVarIntentionalAllocFail) { 600 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 601 env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2}.set_discovery_type(ManifestDiscoveryType::env_var)); 602 603 const char* layer_name = "VK_LAYER_implicit"; 604 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 605 .set_name(layer_name) 606 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 607 .set_disable_environment("DISABLE_ENV")), 608 "test_layer.json"); 609 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 610 611 env.env_var_vk_icd_filenames.add_to_list((fs::path("totally_made_up") / "path_to_fake" / "jason_file.json").str()); 612 size_t fail_index = 0; 613 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 614 while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) { 615 MemoryTracker tracker({false, 0, true, fail_index}); 616 617 VkInstance instance; 618 InstanceCreateInfo inst_create_info{}; 619 result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance); 620 if (result == VK_SUCCESS) { 621 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 622 } 623 ASSERT_TRUE(tracker.empty()); 624 fail_index++; 625 } 626} 627 628// Test failure during vkCreateDevice to make sure we don't leak memory if 629// one of the out-of-memory conditions trigger. 630// Use 2 physical devices so that anything which copies a list of devices item by item 631// may fail. 632TEST(Allocation, CreateDeviceIntentionalAllocFail) { 633 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 634 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) 635 .add_physical_device(PhysicalDevice{"physical_device_0"} 636 .add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, false}) 637 .finish()) 638 .add_physical_device(PhysicalDevice{"physical_device_1"} 639 .add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, false}) 640 .finish()); 641 642 const char* layer_name = "VK_LAYER_implicit"; 643 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 644 .set_name(layer_name) 645 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 646 .set_disable_environment("DISABLE_ENV")), 647 "test_layer.json"); 648 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 649 650 InstWrapper inst{env.vulkan_functions}; 651 ASSERT_NO_FATAL_FAILURE(inst.CheckCreate()); 652 653 uint32_t physical_count = 2; 654 uint32_t returned_physical_count = 0; 655 ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, nullptr)); 656 ASSERT_EQ(physical_count, returned_physical_count); 657 658 VkPhysicalDevice physical_devices[2]; 659 ASSERT_EQ(VK_SUCCESS, inst->vkEnumeratePhysicalDevices(inst.inst, &returned_physical_count, physical_devices)); 660 ASSERT_EQ(physical_count, returned_physical_count); 661 662 uint32_t family_count = 1; 663 uint32_t returned_family_count = 0; 664 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[0], &returned_family_count, nullptr); 665 ASSERT_EQ(returned_family_count, family_count); 666 667 VkQueueFamilyProperties family; 668 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[0], &returned_family_count, &family); 669 ASSERT_EQ(returned_family_count, family_count); 670 ASSERT_EQ(family.queueFlags, static_cast<VkQueueFlags>(VK_QUEUE_GRAPHICS_BIT)); 671 ASSERT_EQ(family.queueCount, family_count); 672 ASSERT_EQ(family.timestampValidBits, 0U); 673 674 size_t fail_index = 0; 675 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 676 while (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 677 MemoryTracker tracker({false, 0, true, fail_index}); 678 679 DeviceCreateInfo dev_create_info; 680 dev_create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f)); 681 682 VkDevice device; 683 result = inst->vkCreateDevice(physical_devices[0], dev_create_info.get(), tracker.get(), &device); 684 if (result == VK_SUCCESS || fail_index > 10000) { 685 VkQueue queue; 686 inst->vkGetDeviceQueue(device, 0, 0, &queue); 687 688 inst->vkDestroyDevice(device, tracker.get()); 689 break; 690 } 691 ASSERT_TRUE(tracker.empty()); 692 fail_index++; 693 } 694} 695 696// Test failure during vkCreateInstance and vkCreateDevice to make sure we don't 697// leak memory if one of the out-of-memory conditions trigger. 698// Includes drivers with several instance extensions, drivers that will fail to load, directly loaded drivers 699TEST(Allocation, CreateInstanceDeviceIntentionalAllocFail) { 700 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 701 uint32_t num_physical_devices = 4; 702 uint32_t num_implicit_layers = 3; 703 for (uint32_t i = 0; i < num_physical_devices; i++) { 704 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2) 705 .icd_manifest.set_is_portability_driver(false) 706 .set_library_arch(sizeof(void*) == 8 ? "64" : "32")) 707 .set_icd_api_version(VK_API_VERSION_1_1) 708 .add_instance_extension("VK_KHR_get_physical_device_properties2") 709 .add_physical_device("physical_device_0") 710 .physical_devices.at(0) 711 .add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, false}) 712 .add_extensions({"VK_EXT_one", "VK_EXT_two", "VK_EXT_three", "VK_EXT_four", "VK_EXT_five"}); 713 } 714 715 env.add_icd(TestICDDetails(CURRENT_PLATFORM_DUMMY_BINARY_WRONG_TYPE).set_is_fake(true)); 716 717 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none)); 718 719 VkDirectDriverLoadingInfoLUNARG ddl_info{}; 720 ddl_info.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_INFO_LUNARG; 721 ddl_info.pfnGetInstanceProcAddr = env.icds.back().icd_library.get_symbol("vk_icdGetInstanceProcAddr"); 722 723 VkDirectDriverLoadingListLUNARG ddl_list{}; 724 ddl_list.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG; 725 ddl_list.mode = VK_DIRECT_DRIVER_LOADING_MODE_INCLUSIVE_LUNARG; 726 ddl_list.driverCount = 1; 727 ddl_list.pDrivers = &ddl_info; 728 729 const char* layer_name = "VK_LAYER_ImplicitAllocFail"; 730 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 731 .set_name(layer_name) 732 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 733 .set_disable_environment("DISABLE_ENV")), 734 "test_layer.json"); 735 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 736 for (uint32_t i = 1; i < num_implicit_layers + 1; i++) { 737 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 738 .set_name("VK_LAYER_Implicit1" + std::to_string(i)) 739 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 740 .set_disable_environment("DISABLE_ENV")), 741 "test_layer_" + std::to_string(i) + ".json"); 742 } 743 std::fstream custom_json_file{COMPLEX_JSON_FILE, std::ios_base::in}; 744 ASSERT_TRUE(custom_json_file.is_open()); 745 std::stringstream custom_json_file_contents; 746 custom_json_file_contents << custom_json_file.rdbuf(); 747 748 fs::path new_path = env.get_folder(ManifestLocation::explicit_layer) 749 .write_manifest("VK_LAYER_complex_file.json", custom_json_file_contents.str()); 750 env.platform_shim->add_manifest(ManifestCategory::explicit_layer, new_path); 751 752 size_t fail_index = 0; 753 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 754 while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) { 755 MemoryTracker tracker{{false, 0, true, fail_index}}; 756 fail_index++; // applies to the next loop 757 758 VkInstance instance; 759 InstanceCreateInfo inst_create_info{}; 760 inst_create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME); 761 inst_create_info.instance_info.pNext = reinterpret_cast<const void*>(&ddl_list); 762 result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance); 763 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 764 ASSERT_TRUE(tracker.empty()); 765 continue; 766 } 767 ASSERT_EQ(result, VK_SUCCESS); 768 769 uint32_t returned_physical_count = 0; 770 result = env.vulkan_functions.vkEnumeratePhysicalDevices(instance, &returned_physical_count, nullptr); 771 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 772 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 773 ASSERT_TRUE(tracker.empty()); 774 continue; 775 } 776 ASSERT_EQ(result, VK_SUCCESS); 777 ASSERT_EQ(num_physical_devices, returned_physical_count); 778 779 std::vector<VkPhysicalDevice> physical_devices; 780 physical_devices.resize(returned_physical_count); 781 result = env.vulkan_functions.vkEnumeratePhysicalDevices(instance, &returned_physical_count, physical_devices.data()); 782 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 783 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 784 ASSERT_TRUE(tracker.empty()); 785 continue; 786 } 787 ASSERT_EQ(result, VK_SUCCESS); 788 ASSERT_EQ(num_physical_devices, returned_physical_count); 789 for (uint32_t i = 0; i < returned_physical_count; i++) { 790 uint32_t family_count = 1; 791 uint32_t returned_family_count = 0; 792 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_devices.at(i), &returned_family_count, nullptr); 793 ASSERT_EQ(returned_family_count, family_count); 794 795 VkQueueFamilyProperties family; 796 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_devices.at(i), &returned_family_count, &family); 797 ASSERT_EQ(returned_family_count, family_count); 798 ASSERT_EQ(family.queueFlags, static_cast<VkQueueFlags>(VK_QUEUE_GRAPHICS_BIT)); 799 ASSERT_EQ(family.queueCount, family_count); 800 ASSERT_EQ(family.timestampValidBits, 0U); 801 802 DeviceCreateInfo dev_create_info; 803 dev_create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f)); 804 805 VkDevice device; 806 result = env.vulkan_functions.vkCreateDevice(physical_devices.at(i), dev_create_info.get(), tracker.get(), &device); 807 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 808 break; 809 } 810 ASSERT_EQ(result, VK_SUCCESS); 811 812 VkQueue queue; 813 env.vulkan_functions.vkGetDeviceQueue(device, 0, 0, &queue); 814 815 env.vulkan_functions.vkDestroyDevice(device, tracker.get()); 816 } 817 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 818 819 ASSERT_TRUE(tracker.empty()); 820 } 821} 822 823// Test failure during vkCreateInstance when a driver of the wrong architecture is present 824// to make sure the loader uses the valid ICD and doesn't report incompatible driver just because 825// an incompatible driver exists 826TEST(TryLoadWrongBinaries, CreateInstanceIntentionalAllocFail) { 827 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 828 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); 829 env.add_icd(TestICDDetails(CURRENT_PLATFORM_DUMMY_BINARY_WRONG_TYPE).set_is_fake(true)); 830 831 const char* layer_name = "VK_LAYER_implicit"; 832 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 833 .set_name(layer_name) 834 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 835 .set_disable_environment("DISABLE_ENV")), 836 "test_layer.json"); 837 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 838 839 size_t fail_index = 0; 840 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 841 while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) { 842 MemoryTracker tracker({false, 0, true, fail_index}); 843 844 VkInstance instance; 845 InstanceCreateInfo inst_create_info{}; 846 result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance); 847 if (result == VK_SUCCESS) { 848 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 849 } 850 ASSERT_NE(result, VK_ERROR_INCOMPATIBLE_DRIVER); 851 ASSERT_TRUE(tracker.empty()); 852 fail_index++; 853 } 854} 855 856// Test failure during vkCreateInstance and vkCreateDevice to make sure we don't 857// leak memory if one of the out-of-memory conditions trigger. 858TEST(Allocation, EnumeratePhysicalDevicesIntentionalAllocFail) { 859 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 860 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); 861 862 const char* layer_name = "VK_LAYER_implicit"; 863 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 864 .set_name(layer_name) 865 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 866 .set_disable_environment("DISABLE_ENV")), 867 "test_layer.json"); 868 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 869 870 size_t fail_index = 0; 871 bool reached_the_end = false; 872 uint32_t starting_physical_dev_count = 3; 873 while (!reached_the_end && fail_index <= 100) { 874 fail_index++; // applies to the next loop 875 uint32_t physical_dev_count = starting_physical_dev_count; 876 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 877 auto& driver = env.reset_icd(); 878 879 for (uint32_t i = 0; i < physical_dev_count; i++) { 880 driver.physical_devices.emplace_back(std::string("physical_device_") + std::to_string(i)) 881 .add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, false}); 882 } 883 MemoryTracker tracker{{false, 0, true, fail_index}}; 884 InstanceCreateInfo inst_create_info; 885 VkInstance instance; 886 result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance); 887 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 888 ASSERT_TRUE(tracker.empty()); 889 continue; 890 } 891 892 uint32_t returned_physical_count = 0; 893 result = env.vulkan_functions.vkEnumeratePhysicalDevices(instance, &returned_physical_count, nullptr); 894 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 895 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 896 ASSERT_TRUE(tracker.empty()); 897 continue; 898 } 899 ASSERT_EQ(physical_dev_count, returned_physical_count); 900 901 for (uint32_t i = 0; i < 2; i++) { 902 driver.physical_devices.emplace_back(std::string("physical_device_") + std::to_string(physical_dev_count)) 903 .add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, false}); 904 physical_dev_count += 1; 905 } 906 907 std::vector<VkPhysicalDevice> physical_devices{physical_dev_count, VK_NULL_HANDLE}; 908 result = env.vulkan_functions.vkEnumeratePhysicalDevices(instance, &returned_physical_count, physical_devices.data()); 909 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 910 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 911 ASSERT_TRUE(tracker.empty()); 912 continue; 913 } 914 if (result == VK_INCOMPLETE) { 915 result = env.vulkan_functions.vkEnumeratePhysicalDevices(instance, &returned_physical_count, nullptr); 916 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 917 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 918 ASSERT_TRUE(tracker.empty()); 919 continue; 920 } 921 physical_devices.resize(returned_physical_count); 922 result = env.vulkan_functions.vkEnumeratePhysicalDevices(instance, &returned_physical_count, physical_devices.data()); 923 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 924 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 925 ASSERT_TRUE(tracker.empty()); 926 continue; 927 } 928 } 929 ASSERT_EQ(physical_dev_count, returned_physical_count); 930 931 std::array<VkDevice, 5> devices; 932 for (uint32_t i = 0; i < returned_physical_count; i++) { 933 uint32_t family_count = 1; 934 uint32_t returned_family_count = 0; 935 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &returned_family_count, nullptr); 936 ASSERT_EQ(returned_family_count, family_count); 937 938 VkQueueFamilyProperties family; 939 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &returned_family_count, &family); 940 ASSERT_EQ(returned_family_count, family_count); 941 ASSERT_EQ(family.queueFlags, static_cast<VkQueueFlags>(VK_QUEUE_GRAPHICS_BIT)); 942 ASSERT_EQ(family.queueCount, family_count); 943 ASSERT_EQ(family.timestampValidBits, 0U); 944 945 DeviceCreateInfo dev_create_info; 946 dev_create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f)); 947 948 result = env.vulkan_functions.vkCreateDevice(physical_devices[i], dev_create_info.get(), tracker.get(), &devices[i]); 949 950 VkQueue queue; 951 if (result == VK_SUCCESS) { 952 env.vulkan_functions.vkGetDeviceQueue(devices[i], 0, 0, &queue); 953 } 954 } 955 for (uint32_t i = 0; i < returned_physical_count; i++) { 956 if (result == VK_SUCCESS) { 957 env.vulkan_functions.vkDestroyDevice(devices[i], tracker.get()); 958 } 959 } 960 961 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 962 ASSERT_TRUE(tracker.empty()); 963 reached_the_end = true; 964 } 965} 966#if defined(WIN32) 967// Test failure during vkCreateInstance and vkCreateDevice to make sure we don't 968// leak memory if one of the out-of-memory conditions trigger. 969TEST(Allocation, CreateInstanceDeviceWithDXGIDriverIntentionalAllocFail) { 970 FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")}; 971 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_6).set_discovery_type(ManifestDiscoveryType::null_dir)); 972 env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); 973 974 for (uint32_t i = 0; i < 2; i++) { 975 auto& driver = env.get_test_icd(i); 976 driver.physical_devices.emplace_back(std::string("physical_device_") + std::to_string(i)) 977 .add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, false}); 978 } 979 980 const char* layer_name = "VK_LAYER_implicit"; 981 env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} 982 .set_name(layer_name) 983 .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) 984 .set_disable_environment("DISABLE_ENV")), 985 "test_layer.json"); 986 env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true); 987 988 auto& known_driver = known_driver_list.at(2); // which drive this test pretends to be 989 DXGI_ADAPTER_DESC1 desc1{}; 990 desc1.VendorId = known_driver.vendor_id; 991 desc1.AdapterLuid = _LUID{10, 1000}; 992 env.platform_shim->add_dxgi_adapter(GpuType::discrete, desc1); 993 env.get_test_icd(0).set_adapterLUID(desc1.AdapterLuid); 994 995 env.platform_shim->add_d3dkmt_adapter(D3DKMT_Adapter{0, _LUID{10, 1000}}.add_driver_manifest_path(env.get_icd_manifest_path())); 996 997 size_t fail_index = 0; 998 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY; 999 while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) { 1000 MemoryTracker tracker({false, 0, true, fail_index}); 1001 fail_index++; // applies to the next loop 1002 1003 VkInstance instance; 1004 InstanceCreateInfo inst_create_info{}; 1005 result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance); 1006 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 1007 ASSERT_TRUE(tracker.empty()); 1008 continue; 1009 } 1010 1011 uint32_t physical_count = 2; 1012 uint32_t returned_physical_count = 0; 1013 result = env.vulkan_functions.vkEnumeratePhysicalDevices(instance, &returned_physical_count, nullptr); 1014 if (result == VK_ERROR_OUT_OF_HOST_MEMORY || result == VK_INCOMPLETE) { 1015 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 1016 ASSERT_TRUE(tracker.empty()); 1017 continue; 1018 } 1019 ASSERT_EQ(physical_count, returned_physical_count); 1020 1021 std::array<VkPhysicalDevice, 2> physical_devices; 1022 result = env.vulkan_functions.vkEnumeratePhysicalDevices(instance, &returned_physical_count, physical_devices.data()); 1023 if (result == VK_ERROR_OUT_OF_HOST_MEMORY || result == VK_INCOMPLETE) { 1024 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 1025 ASSERT_TRUE(tracker.empty()); 1026 continue; 1027 } 1028 ASSERT_EQ(physical_count, returned_physical_count); 1029 1030 std::array<VkDevice, 2> devices; 1031 for (uint32_t i = 0; i < returned_physical_count; i++) { 1032 uint32_t family_count = 1; 1033 uint32_t returned_family_count = 0; 1034 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &returned_family_count, nullptr); 1035 ASSERT_EQ(returned_family_count, family_count); 1036 1037 VkQueueFamilyProperties family; 1038 env.vulkan_functions.vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &returned_family_count, &family); 1039 ASSERT_EQ(returned_family_count, family_count); 1040 ASSERT_EQ(family.queueFlags, static_cast<VkQueueFlags>(VK_QUEUE_GRAPHICS_BIT)); 1041 ASSERT_EQ(family.queueCount, family_count); 1042 ASSERT_EQ(family.timestampValidBits, 0U); 1043 1044 DeviceCreateInfo dev_create_info; 1045 dev_create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f)); 1046 1047 result = env.vulkan_functions.vkCreateDevice(physical_devices[i], dev_create_info.get(), tracker.get(), &devices[i]); 1048 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) { 1049 devices[i] = VK_NULL_HANDLE; 1050 } else { 1051 VkQueue queue; 1052 env.vulkan_functions.vkGetDeviceQueue(devices[i], 0, 0, &queue); 1053 } 1054 } 1055 for (uint32_t i = 0; i < returned_physical_count; i++) { 1056 if (devices[i] != VK_NULL_HANDLE) { 1057 env.vulkan_functions.vkDestroyDevice(devices[i], tracker.get()); 1058 } 1059 } 1060 env.vulkan_functions.vkDestroyInstance(instance, tracker.get()); 1061 1062 ASSERT_TRUE(tracker.empty()); 1063 } 1064} 1065#endif 1066