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