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
32 struct 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
39 class 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
allocate(size_t size, size_t alignment, VkSystemAllocationScope alloc_scope)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 }
reallocate(void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope alloc_scope)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 }
free(void* pMemory)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:
settings(settings)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.
TEST(Allocation, Instance)181 TEST(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.
TEST(Allocation, GetInstanceProcAddr)195 TEST(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.
TEST(Allocation, EnumeratePhysicalDevices)213 TEST(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.
TEST(Allocation, InstanceAndDevice)236 TEST(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.
TEST(Allocation, InstanceButNotDevice)285 TEST(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.
TEST(Allocation, DeviceButNotInstance)334 TEST(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.
TEST(Allocation, CreateInstanceIntentionalAllocFail)391 TEST(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
TEST(Allocation, CreateInstanceIntentionalAllocFailInvalidManifests)421 TEST(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.
TEST(Allocation, CreateSurfaceIntentionalAllocFail)466 TEST(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.
TEST(Allocation, CreateInstanceIntentionalAllocFailWithSettingsFilePresent)511 TEST(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.
TEST(Allocation, CreateSurfaceIntentionalAllocFailWithSettingsFilePresent)548 TEST(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.
TEST(Allocation, DriverEnvVarIntentionalAllocFail)599 TEST(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.
TEST(Allocation, CreateDeviceIntentionalAllocFail)632 TEST(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
TEST(Allocation, CreateInstanceDeviceIntentionalAllocFail)699 TEST(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
TEST(TryLoadWrongBinaries, CreateInstanceIntentionalAllocFail)826 TEST(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.
TEST(Allocation, EnumeratePhysicalDevicesIntentionalAllocFail)858 TEST(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.
TEST(Allocation, CreateInstanceDeviceWithDXGIDriverIntentionalAllocFail)969 TEST(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