/*
 * Copyright (c) 2021-2023 The Khronos Group Inc.
 * Copyright (c) 2021-2023 Valve Corporation
 * Copyright (c) 2021-2023 LunarG, Inc.
 * Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * Copyright (c) 2023-2023 RasterGrid Kft.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and/or associated documentation files (the "Materials"), to
 * deal in the Materials without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Materials, and to permit persons to whom the Materials are
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice(s) and this permission notice shall be included in
 * all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
 * USE OR OTHER DEALINGS IN THE MATERIALS.
 *
 * Author: Charles Giessen <charles@lunarg.com>
 */

#include "test_icd.h"

#include <assert.h>
#include <cstring>

#include <iostream>

#include "equality_helpers.h"

// export vk_icdGetInstanceProcAddr
#if !defined(TEST_ICD_EXPORT_ICD_GIPA)
#define TEST_ICD_EXPORT_ICD_GIPA 0
#endif

// export vk_icdNegotiateLoaderICDInterfaceVersion
#if !defined(TEST_ICD_EXPORT_NEGOTIATE_INTERFACE_VERSION)
#define TEST_ICD_EXPORT_NEGOTIATE_INTERFACE_VERSION 0
#endif

// export vk_icdGetPhysicalDeviceProcAddr
#if !defined(TEST_ICD_EXPORT_ICD_GPDPA)
#define TEST_ICD_EXPORT_ICD_GPDPA 0
#endif

// export vk_icdEnumerateAdapterPhysicalDevices
#if !defined(TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES)
#define TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES 0
#endif

// export vk_icdNegotiateLoaderICDInterfaceVersion, vk_icdEnumerateAdapterPhysicalDevices, and vk_icdGetPhysicalDeviceProcAddr
// through dlsym/GetProcAddress
// Default is *on*
#if !defined(TEST_ICD_EXPORT_VERSION_7)
#define TEST_ICD_EXPORT_VERSION_7 1
#endif

TestICD icd;
extern "C" {
FRAMEWORK_EXPORT TestICD* get_test_icd_func() { return &icd; }
FRAMEWORK_EXPORT TestICD* reset_icd_func() {
    icd.~TestICD();
    return new (&icd) TestICD();
}
}

LayerDefinition& FindLayer(std::vector<LayerDefinition>& layers, std::string layerName) {
    for (auto& layer : layers) {
        if (layer.layerName == layerName) return layer;
    }
    assert(false && "Layer name not found!");
    return layers[0];
}
bool CheckLayer(std::vector<LayerDefinition>& layers, std::string layerName) {
    for (auto& layer : layers) {
        if (layer.layerName == layerName) return true;
    }
    return false;
}

bool IsInstanceExtensionSupported(const char* extension_name) {
    return icd.instance_extensions.end() !=
           std::find_if(icd.instance_extensions.begin(), icd.instance_extensions.end(),
                        [extension_name](Extension const& ext) { return string_eq(&ext.extensionName[0], extension_name); });
}

bool IsInstanceExtensionEnabled(const char* extension_name) {
    return icd.enabled_instance_extensions.end() !=
           std::find_if(icd.enabled_instance_extensions.begin(), icd.enabled_instance_extensions.end(),
                        [extension_name](Extension const& ext) { return string_eq(&ext.extensionName[0], extension_name); });
}

bool IsPhysicalDeviceExtensionAvailable(const char* extension_name) {
    for (auto const& [phys_dev_handle, phys_dev] : icd.physical_devices) {
        if (phys_dev.extensions.end() !=
            std::find_if(phys_dev.extensions.begin(), phys_dev.extensions.end(),
                         [extension_name](Extension const& ext) { return ext.extensionName == extension_name; })) {
            return true;
        }
    }
    return false;
}

PhysicalDevice& GetPhysDevice(VkPhysicalDevice physicalDevice) {
    if (icd.physical_devices.count(physicalDevice) > 0) {
        return icd.physical_devices.at(physicalDevice);
    }
    assert(false && "vkPhysicalDevice not found!");
    return icd.physical_devices.at(icd.physical_devices.begin()->first);
}

bool is_valid_surface(VkSurfaceKHR surface_to_check) {
    uint64_t surf_handle = (uint64_t)(surface_to_check);
    auto found = std::find(icd.surface_handles.begin(), icd.surface_handles.end(), surf_handle);
    return found != icd.surface_handles.end();
}

struct FindDevice {
    bool found = false;
    VkPhysicalDevice phys_dev = 0;
    uint32_t dev_index = 0;
};

FindDevice lookup_device(VkDevice device) {
    FindDevice fd{};
    auto it = icd.device_to_physical_device_map.find(device);
    if (it != icd.device_to_physical_device_map.end()) {
        auto& phys_dev = icd.physical_devices.at(it->second);
        fd.found = true;
        fd.phys_dev = phys_dev.vk_physical_device.handle;
        for (uint32_t d = 0; d < phys_dev.device_handles.size(); d++) {
            if (phys_dev.device_handles.at(d) == device) {
                fd.dev_index = d;
                break;
            }
        }
        return fd;
    }
    return fd;
}

// typename T must have '.get()' function that returns a type U
template <typename T, typename U>
VkResult FillCountPtr(std::vector<T> const& data_vec, uint32_t* pCount, U* pData) {
    if (pCount == nullptr) {
        return VK_ERROR_OUT_OF_HOST_MEMORY;
    }
    if (pData == nullptr) {
        *pCount = static_cast<uint32_t>(data_vec.size());
        return VK_SUCCESS;
    }
    uint32_t amount_written = 0;
    uint32_t amount_to_write = static_cast<uint32_t>(data_vec.size());
    if (*pCount < data_vec.size()) {
        amount_to_write = *pCount;
    }
    for (size_t i = 0; i < amount_to_write; i++) {
        pData[i] = data_vec[i].get();
        amount_written++;
    }
    if (*pCount < data_vec.size()) {
        *pCount = amount_written;
        return VK_INCOMPLETE;
    }
    *pCount = amount_written;
    return VK_SUCCESS;
}

template <typename T>
VkResult FillCountPtr(std::vector<T> const& data_vec, uint32_t* pCount, T* pData) {
    if (pCount == nullptr) {
        return VK_ERROR_OUT_OF_HOST_MEMORY;
    }
    if (pData == nullptr) {
        *pCount = static_cast<uint32_t>(data_vec.size());
        return VK_SUCCESS;
    }
    uint32_t amount_written = 0;
    uint32_t amount_to_write = static_cast<uint32_t>(data_vec.size());
    if (*pCount < data_vec.size()) {
        amount_to_write = *pCount;
    }
    for (size_t i = 0; i < amount_to_write; i++) {
        pData[i] = data_vec[i];
        amount_written++;
    }
    if (*pCount < data_vec.size()) {
        *pCount = amount_written;
        return VK_INCOMPLETE;
    }
    *pCount = amount_written;
    return VK_SUCCESS;
}

void check_allocator_handle(const VkAllocationCallbacks* pAllocator) {
    if (pAllocator) {
        if (nullptr == pAllocator->pfnAllocation || nullptr == pAllocator->pfnFree || nullptr == pAllocator->pfnReallocation) {
            std::cout << "pAllocator functions are NULL!\n";
            abort();
        }
    }
}

//// Instance Functions ////

// VK_SUCCESS,VK_INCOMPLETE
VKAPI_ATTR VkResult VKAPI_CALL test_vkEnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount,
                                                                           VkExtensionProperties* pProperties) {
    if (pLayerName != nullptr) {
        auto& layer = FindLayer(icd.instance_layers, std::string(pLayerName));
        return FillCountPtr(layer.extensions, pPropertyCount, pProperties);
    } else {  // instance extensions
        FillCountPtr(icd.instance_extensions, pPropertyCount, pProperties);
    }

    return VK_SUCCESS;
}

VKAPI_ATTR VkResult VKAPI_CALL test_vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties) {
    return FillCountPtr(icd.instance_layers, pPropertyCount, pProperties);
}

VKAPI_ATTR VkResult VKAPI_CALL test_vkEnumerateInstanceVersion(uint32_t* pApiVersion) {
    if (pApiVersion != nullptr) {
        *pApiVersion = icd.icd_api_version;
    }
    return VK_SUCCESS;
}

VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo,
                                                     const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) {
    if (pCreateInfo == nullptr) {
        return VK_ERROR_OUT_OF_HOST_MEMORY;
    }
    check_allocator_handle(pAllocator);

    uint32_t default_api_version = VK_API_VERSION_1_0;
    uint32_t api_version =
        (pCreateInfo->pApplicationInfo == nullptr) ? default_api_version : pCreateInfo->pApplicationInfo->apiVersion;

    if (icd.icd_api_version < VK_API_VERSION_1_1) {
        if (api_version > VK_API_VERSION_1_0) {
            return VK_ERROR_INCOMPATIBLE_DRIVER;
        }
    }

    // Add to the list of enabled extensions only those that the ICD actively supports
    icd.enabled_instance_extensions.clear();
    for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
        if (IsInstanceExtensionSupported(pCreateInfo->ppEnabledExtensionNames[i])) {
            icd.enabled_instance_extensions.push_back({pCreateInfo->ppEnabledExtensionNames[i]});
        }
    }

    // VK_SUCCESS
    *pInstance = icd.instance_handle.handle;

    icd.passed_in_instance_create_flags = pCreateInfo->flags;

    return VK_SUCCESS;
}

VKAPI_ATTR void VKAPI_CALL test_vkDestroyInstance([[maybe_unused]] VkInstance instance, const VkAllocationCallbacks* pAllocator) {
    icd.enabled_instance_extensions.clear();
    check_allocator_handle(pAllocator);
}

// VK_SUCCESS,VK_INCOMPLETE
VKAPI_ATTR VkResult VKAPI_CALL test_vkEnumeratePhysicalDevices([[maybe_unused]] VkInstance instance, uint32_t* pPhysicalDeviceCount,
                                                               VkPhysicalDevice* pPhysicalDevices) {
    if (icd.enum_physical_devices_return_code != VK_SUCCESS) {
        return icd.enum_physical_devices_return_code;
    }
    if (pPhysicalDevices == nullptr) {
        *pPhysicalDeviceCount = static_cast<uint32_t>(icd.physical_devices.size());
    } else {
        using SortPair = std::pair<size_t, VkPhysicalDevice>;
        std::vector<SortPair> in_order_phys_dev;
        for (auto const& [phys_dev_handle, phys_dev] : icd.physical_devices) {
            in_order_phys_dev.push_back({phys_dev.iteration_order, phys_dev_handle});
        }

        std::sort(in_order_phys_dev.begin(), in_order_phys_dev.end(),
                  [](SortPair const& a, SortPair const& b) { return a.first < b.first; });

        uint32_t handles_written = 0;
        for (auto const& phys_dev : in_order_phys_dev) {
            if (handles_written + 1 > *pPhysicalDeviceCount) {
                *pPhysicalDeviceCount = handles_written;
                return VK_INCOMPLETE;
            }
            pPhysicalDevices[handles_written++] = phys_dev.second;
        }
        *pPhysicalDeviceCount = handles_written;
    }
    return VK_SUCCESS;
}

// VK_SUCCESS,VK_INCOMPLETE, VK_ERROR_INITIALIZATION_FAILED
VKAPI_ATTR VkResult VKAPI_CALL
test_vkEnumeratePhysicalDeviceGroups([[maybe_unused]] VkInstance instance, uint32_t* pPhysicalDeviceGroupCount,
                                     VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
    VkResult result = VK_SUCCESS;

    if (pPhysicalDeviceGroupProperties == nullptr) {
        if (0 == icd.physical_device_groups.size()) {
            *pPhysicalDeviceGroupCount = static_cast<uint32_t>(icd.physical_devices.size());
        } else {
            *pPhysicalDeviceGroupCount = static_cast<uint32_t>(icd.physical_device_groups.size());
        }
    } else {
        // NOTE: This is a fake struct to make sure the pNext chain is properly passed down to the ICD
        //       vkEnumeratePhysicalDeviceGroups.
        //       The two versions must match:
        //           "FakePNext" test in loader_regresion_tests.cpp
        //           "test_vkEnumeratePhysicalDeviceGroups" in test_icd.cpp
        struct FakePnextSharedWithICD {
            VkStructureType sType;
            void* pNext;
            uint32_t value;
        };

        uint32_t group_count = 0;
        if (0 == icd.physical_device_groups.size()) {
            group_count = static_cast<uint32_t>(icd.physical_devices.size());
            for (size_t device_group = 0; device_group < icd.physical_devices.size(); device_group++) {
                if (device_group >= *pPhysicalDeviceGroupCount) {
                    group_count = *pPhysicalDeviceGroupCount;
                    result = VK_INCOMPLETE;
                    break;
                }

                pPhysicalDeviceGroupProperties[device_group].subsetAllocation = false;
                pPhysicalDeviceGroupProperties[device_group].physicalDeviceCount = 1;
                for (auto const& [phys_dev_handle, phys_dev] : icd.physical_devices) {
                    if (phys_dev.iteration_order == device_group) {
                        pPhysicalDeviceGroupProperties[device_group].physicalDevices[0] = phys_dev_handle;
                        break;
                    }
                }
                for (size_t i = 1; i < VK_MAX_DEVICE_GROUP_SIZE; i++) {
                    pPhysicalDeviceGroupProperties[device_group].physicalDevices[i] = {};
                }
            }
        } else {
            group_count = static_cast<uint32_t>(icd.physical_device_groups.size());
            for (size_t device_group = 0; device_group < icd.physical_device_groups.size(); device_group++) {
                if (device_group >= *pPhysicalDeviceGroupCount) {
                    group_count = *pPhysicalDeviceGroupCount;
                    result = VK_INCOMPLETE;
                    break;
                }
                pPhysicalDeviceGroupProperties[device_group].subsetAllocation =
                    icd.physical_device_groups[device_group].subset_allocation;
                uint32_t handles_written = 0;
                for (size_t i = 0; i < icd.physical_device_groups[device_group].physical_device_handles.size(); i++) {
                    handles_written++;
                    pPhysicalDeviceGroupProperties[device_group].physicalDevices[i] =
                        icd.physical_device_groups[device_group].physical_device_handles[i]->vk_physical_device.handle;
                }
                for (size_t i = handles_written; i < VK_MAX_DEVICE_GROUP_SIZE; i++) {
                    pPhysicalDeviceGroupProperties[device_group].physicalDevices[i] = {};
                }
                pPhysicalDeviceGroupProperties[device_group].physicalDeviceCount = handles_written;
            }
        }
        // NOTE: The following code is purely to test pNext passing in vkEnumeratePhysicalDevice groups
        //       and includes normally invalid information.
        for (size_t device_group = 0; device_group < group_count; device_group++) {
            if (nullptr != pPhysicalDeviceGroupProperties[device_group].pNext) {
                VkBaseInStructure* base = reinterpret_cast<VkBaseInStructure*>(pPhysicalDeviceGroupProperties[device_group].pNext);
                if (base->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT) {
                    FakePnextSharedWithICD* fake = reinterpret_cast<FakePnextSharedWithICD*>(base);
                    fake->value = 0xDECAFBAD;
                }
            }
        }
        *pPhysicalDeviceGroupCount = static_cast<uint32_t>(group_count);
    }
    return result;
}

VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDebugUtilsMessengerEXT(
    [[maybe_unused]] VkInstance instance, [[maybe_unused]] const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
    const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pMessenger) {
    if (nullptr != pMessenger) {
        uint8_t* new_handle_ptr = nullptr;
        if (pAllocator) {
            new_handle_ptr =
                (uint8_t*)pAllocator->pfnAllocation(pAllocator->pUserData, sizeof(uint8_t*), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
        } else {
            new_handle_ptr = new uint8_t;
        }
        uint64_t fake_msgr_handle = reinterpret_cast<uint64_t>(new_handle_ptr);
        icd.messenger_handles.push_back(fake_msgr_handle);
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || \
    defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
        *pMessenger = reinterpret_cast<VkDebugUtilsMessengerEXT>(fake_msgr_handle);
#else
        *pMessenger = fake_msgr_handle;
#endif
    }
    return VK_SUCCESS;
}

VKAPI_ATTR void VKAPI_CALL test_vkDestroyDebugUtilsMessengerEXT([[maybe_unused]] VkInstance instance,
                                                                VkDebugUtilsMessengerEXT messenger,
                                                                const VkAllocationCallbacks* pAllocator) {
    if (messenger != VK_NULL_HANDLE) {
        uint64_t fake_msgr_handle = (uint64_t)(messenger);
        auto found_iter = std::find(icd.messenger_handles.begin(), icd.messenger_handles.end(), fake_msgr_handle);
        if (found_iter != icd.messenger_handles.end()) {
            // Remove it from the list
            icd.messenger_handles.erase(found_iter);
            // Delete the handle
            if (pAllocator) {
                pAllocator->pfnFree(pAllocator->pUserData, (uint8_t*)fake_msgr_handle);
            } else {
                delete (uint8_t*)(fake_msgr_handle);
            }
        } else {
            std::cerr << "Messenger not found during destroy!\n";
            abort();
        }
    }
}

// debug report create/destroy

VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDebugReportCallbackEXT(
    [[maybe_unused]] VkInstance instance, [[maybe_unused]] const VkDebugReportCallbackCreateInfoEXT* pCreateInfo,
    const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) {
    if (nullptr != pCallback) {
        uint8_t* new_handle_ptr = nullptr;
        if (pAllocator) {
            new_handle_ptr =
                (uint8_t*)pAllocator->pfnAllocation(pAllocator->pUserData, sizeof(uint8_t*), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
        } else {
            new_handle_ptr = new uint8_t;
        }
        uint64_t fake_msgr_handle = reinterpret_cast<uint64_t>(new_handle_ptr);
        icd.callback_handles.push_back(fake_msgr_handle);
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || \
    defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
        *pCallback = reinterpret_cast<VkDebugReportCallbackEXT>(fake_msgr_handle);
#else
        *pCallback = fake_msgr_handle;
#endif
    }
    return VK_SUCCESS;
}

VKAPI_ATTR void VKAPI_CALL test_vkDestroyDebugReportCallbackEXT([[maybe_unused]] VkInstance instance,
                                                                VkDebugReportCallbackEXT callback,
                                                                const VkAllocationCallbacks* pAllocator) {
    if (callback != VK_NULL_HANDLE) {
        uint64_t fake_msgr_handle = (uint64_t)(callback);
        auto found_iter = std::find(icd.callback_handles.begin(), icd.callback_handles.end(), fake_msgr_handle);
        if (found_iter != icd.callback_handles.end()) {
            // Remove it from the list
            icd.callback_handles.erase(found_iter);
            // Delete the handle
            if (pAllocator) {
                pAllocator->pfnFree(pAllocator->pUserData, (uint8_t*)fake_msgr_handle);
            } else {
                delete (uint8_t*)(fake_msgr_handle);
            }
        } else {
            std::cerr << "callback not found during destroy!\n";
            abort();
        }
    }
}

// Debug utils & debug marker ext stubs
VKAPI_ATTR VkResult VKAPI_CALL test_vkDebugMarkerSetObjectTagEXT(VkDevice dev, const VkDebugMarkerObjectTagInfoEXT* pTagInfo) {
    if (pTagInfo && pTagInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT) {
        VkPhysicalDevice pd = (VkPhysicalDevice)(uintptr_t)(pTagInfo->object);
        if (pd != lookup_device(dev).phys_dev) return VK_ERROR_DEVICE_LOST;
    }
    if (pTagInfo && pTagInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT) {
        if (pTagInfo->object != icd.surface_handles.at(0)) return VK_ERROR_DEVICE_LOST;
    }
    if (pTagInfo && pTagInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT) {
        if (pTagInfo->object != (uint64_t)(uintptr_t)icd.instance_handle.handle) return VK_ERROR_DEVICE_LOST;
    }
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkDebugMarkerSetObjectNameEXT(VkDevice dev, const VkDebugMarkerObjectNameInfoEXT* pNameInfo) {
    if (pNameInfo && pNameInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT) {
        VkPhysicalDevice pd = (VkPhysicalDevice)(uintptr_t)(pNameInfo->object);
        if (pd != lookup_device(dev).phys_dev) return VK_ERROR_DEVICE_LOST;
    }
    if (pNameInfo && pNameInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT) {
        if (pNameInfo->object != icd.surface_handles.at(0)) return VK_ERROR_DEVICE_LOST;
    }
    if (pNameInfo && pNameInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT) {
        if (pNameInfo->object != (uint64_t)(uintptr_t)icd.instance_handle.handle) return VK_ERROR_DEVICE_LOST;
    }
    return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL test_vkCmdDebugMarkerBeginEXT(VkCommandBuffer, const VkDebugMarkerMarkerInfoEXT*) {}
VKAPI_ATTR void VKAPI_CALL test_vkCmdDebugMarkerEndEXT(VkCommandBuffer) {}
VKAPI_ATTR void VKAPI_CALL test_vkCmdDebugMarkerInsertEXT(VkCommandBuffer, const VkDebugMarkerMarkerInfoEXT*) {}

VKAPI_ATTR VkResult VKAPI_CALL test_vkSetDebugUtilsObjectNameEXT(VkDevice dev, const VkDebugUtilsObjectNameInfoEXT* pNameInfo) {
    if (pNameInfo && pNameInfo->objectType == VK_OBJECT_TYPE_PHYSICAL_DEVICE) {
        VkPhysicalDevice pd = (VkPhysicalDevice)(uintptr_t)(pNameInfo->objectHandle);
        if (pd != lookup_device(dev).phys_dev) return VK_ERROR_DEVICE_LOST;
    }
    if (pNameInfo && pNameInfo->objectType == VK_OBJECT_TYPE_SURFACE_KHR) {
        if (pNameInfo->objectHandle != icd.surface_handles.at(0)) return VK_ERROR_DEVICE_LOST;
    }
    if (pNameInfo && pNameInfo->objectType == VK_OBJECT_TYPE_INSTANCE) {
        if (pNameInfo->objectHandle != (uint64_t)(uintptr_t)icd.instance_handle.handle) return VK_ERROR_DEVICE_LOST;
    }
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkSetDebugUtilsObjectTagEXT(VkDevice dev, const VkDebugUtilsObjectTagInfoEXT* pTagInfo) {
    if (pTagInfo && pTagInfo->objectType == VK_OBJECT_TYPE_PHYSICAL_DEVICE) {
        VkPhysicalDevice pd = (VkPhysicalDevice)(uintptr_t)(pTagInfo->objectHandle);
        if (pd != lookup_device(dev).phys_dev) return VK_ERROR_DEVICE_LOST;
    }
    if (pTagInfo && pTagInfo->objectType == VK_OBJECT_TYPE_SURFACE_KHR) {
        if (pTagInfo->objectHandle != icd.surface_handles.at(0)) return VK_ERROR_DEVICE_LOST;
    }
    if (pTagInfo && pTagInfo->objectType == VK_OBJECT_TYPE_INSTANCE) {
        if (pTagInfo->objectHandle != (uint64_t)(uintptr_t)icd.instance_handle.handle) return VK_ERROR_DEVICE_LOST;
    }
    return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL test_vkQueueBeginDebugUtilsLabelEXT(VkQueue, const VkDebugUtilsLabelEXT*) {}
VKAPI_ATTR void VKAPI_CALL test_vkQueueEndDebugUtilsLabelEXT(VkQueue) {}
VKAPI_ATTR void VKAPI_CALL test_vkQueueInsertDebugUtilsLabelEXT(VkQueue, const VkDebugUtilsLabelEXT*) {}
VKAPI_ATTR void VKAPI_CALL test_vkCmdBeginDebugUtilsLabelEXT(VkCommandBuffer, const VkDebugUtilsLabelEXT*) {}
VKAPI_ATTR void VKAPI_CALL test_vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer) {}
VKAPI_ATTR void VKAPI_CALL test_vkCmdInsertDebugUtilsLabelEXT(VkCommandBuffer, const VkDebugUtilsLabelEXT*) {}

//// Physical Device functions ////

// VK_SUCCESS,VK_INCOMPLETE
VKAPI_ATTR VkResult VKAPI_CALL test_vkEnumerateDeviceLayerProperties(VkPhysicalDevice, uint32_t*, VkLayerProperties*) {
    assert(false && "ICD's don't contain layers???");
    return VK_SUCCESS;
}

// VK_SUCCESS, VK_INCOMPLETE, VK_ERROR_LAYER_NOT_PRESENT
VKAPI_ATTR VkResult VKAPI_CALL test_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName,
                                                                         uint32_t* pPropertyCount,
                                                                         VkExtensionProperties* pProperties) {
    auto& phys_dev = GetPhysDevice(physicalDevice);
    if (pLayerName != nullptr) {
        assert(false && "Drivers don't contain layers???");
        return VK_SUCCESS;
    } else {  // instance extensions
        return FillCountPtr(phys_dev.extensions, pPropertyCount, pProperties);
    }
}

VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice,
                                                                         uint32_t* pQueueFamilyPropertyCount,
                                                                         VkQueueFamilyProperties* pQueueFamilyProperties) {
    auto& phys_dev = GetPhysDevice(physicalDevice);
    FillCountPtr(phys_dev.queue_family_properties, pQueueFamilyPropertyCount, pQueueFamilyProperties);
}

VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo,
                                                   const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) {
    check_allocator_handle(pAllocator);
    if (icd.physical_devices.count(physicalDevice) == 0) {
        return VK_ERROR_INITIALIZATION_FAILED;
    }
    auto& found = icd.physical_devices.at(physicalDevice);
    auto device_handle = DispatchableHandle<VkDevice>();
    *pDevice = device_handle.handle;
    found.device_handles.push_back(device_handle.handle);
    found.device_create_infos.emplace_back(pCreateInfo);
    for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) {
        found.queue_handles.emplace_back();
    }
    icd.device_to_physical_device_map.emplace(device_handle.handle, physicalDevice);
    icd.device_handles.emplace_back(std::move(device_handle));
    return VK_SUCCESS;
}

VKAPI_ATTR void VKAPI_CALL test_vkDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
    check_allocator_handle(pAllocator);
    auto found = std::find(icd.device_handles.begin(), icd.device_handles.end(), device);
    if (found != icd.device_handles.end()) icd.device_handles.erase(found);
    auto fd = lookup_device(device);
    if (!fd.found) return;
    auto& phys_dev = icd.physical_devices.at(fd.phys_dev);
    phys_dev.device_handles.erase(phys_dev.device_handles.begin() + fd.dev_index);
    phys_dev.device_create_infos.erase(phys_dev.device_create_infos.begin() + fd.dev_index);
    icd.device_to_physical_device_map.erase(device);
}

VKAPI_ATTR VkResult VKAPI_CALL generic_tool_props_function([[maybe_unused]] VkPhysicalDevice physicalDevice, uint32_t* pToolCount,
                                                           VkPhysicalDeviceToolPropertiesEXT* pToolProperties) {
    if (icd.tooling_properties.size() == 0) {
        return VK_SUCCESS;
    }
    if (pToolProperties == nullptr && pToolCount != nullptr) {
        *pToolCount = static_cast<uint32_t>(icd.tooling_properties.size());
    } else if (pToolCount != nullptr) {
        for (size_t i = 0; i < *pToolCount; i++) {
            if (i >= icd.tooling_properties.size()) {
                return VK_INCOMPLETE;
            }
            pToolProperties[i] = icd.tooling_properties[i];
        }
    }
    return VK_SUCCESS;
}

VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceToolPropertiesEXT(VkPhysicalDevice physicalDevice, uint32_t* pToolCount,
                                                                         VkPhysicalDeviceToolPropertiesEXT* pToolProperties) {
    return generic_tool_props_function(physicalDevice, pToolCount, pToolProperties);
}

VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount,
                                                                      VkPhysicalDeviceToolPropertiesEXT* pToolProperties) {
    return generic_tool_props_function(physicalDevice, pToolCount, pToolProperties);
}

template <typename T>
T to_nondispatch_handle(uint64_t handle) {
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || \
    defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
    return reinterpret_cast<T>(handle);
#else
    return handle;
#endif
}

template <typename T>
uint64_t from_nondispatch_handle(T handle) {
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || \
    defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
    return reinterpret_cast<uint64_t>(handle);
#else
    return handle;
#endif
}

//// WSI ////
template <typename HandleType>
void common_nondispatch_handle_creation(std::vector<uint64_t>& handles, HandleType* pHandle) {
    if (nullptr != pHandle) {
        if (handles.size() == 0)
            handles.push_back(800851234);
        else
            handles.push_back(handles.back() + 102030);
        *pHandle = to_nondispatch_handle<HandleType>(handles.back());
    }
}
#if defined(VK_USE_PLATFORM_ANDROID_KHR)

VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo,
                                                              const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    return VK_SUCCESS;
}
#endif

#if defined(VK_USE_PLATFORM_WIN32_KHR)
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateWin32SurfaceKHR([[maybe_unused]] VkInstance instance,
                                                            [[maybe_unused]] const VkWin32SurfaceCreateInfoKHR* pCreateInfo,
                                                            const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    if (IsInstanceExtensionSupported(VK_KHR_WIN32_SURFACE_EXTENSION_NAME)) {
        common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    }
    return VK_SUCCESS;
}

VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice, uint32_t) { return VK_TRUE; }
#endif  // VK_USE_PLATFORM_WIN32_KHR

#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateWaylandSurfaceKHR([[maybe_unused]] VkInstance instance,
                                                              [[maybe_unused]] const VkWaylandSurfaceCreateInfoKHR* pCreateInfo,
                                                              const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    if (IsInstanceExtensionSupported(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME)) {
        common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    }
    return VK_SUCCESS;
}

VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceWaylandPresentationSupportKHR(VkPhysicalDevice, uint32_t,
                                                                                     struct wl_display*) {
    return VK_TRUE;
}
#endif  // VK_USE_PLATFORM_WAYLAND_KHR
#if defined(VK_USE_PLATFORM_XCB_KHR)
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateXcbSurfaceKHR([[maybe_unused]] VkInstance instance,
                                                          [[maybe_unused]] const VkXcbSurfaceCreateInfoKHR* pCreateInfo,
                                                          const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    if (IsInstanceExtensionSupported(VK_KHR_XCB_SURFACE_EXTENSION_NAME)) {
        common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    }
    return VK_SUCCESS;
}

VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceXcbPresentationSupportKHR(VkPhysicalDevice, uint32_t, xcb_connection_t*,
                                                                                 xcb_visualid_t) {
    return VK_TRUE;
}
#endif  // VK_USE_PLATFORM_XCB_KHR

#if defined(VK_USE_PLATFORM_XLIB_KHR)
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateXlibSurfaceKHR([[maybe_unused]] VkInstance instance,
                                                           [[maybe_unused]] const VkXlibSurfaceCreateInfoKHR* pCreateInfo,
                                                           const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    if (IsInstanceExtensionSupported(VK_KHR_XCB_SURFACE_EXTENSION_NAME)) {
        common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    }
    return VK_SUCCESS;
}

VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceXlibPresentationSupportKHR(VkPhysicalDevice, uint32_t, Display*, VisualID) {
    return VK_TRUE;
}
#endif  // VK_USE_PLATFORM_XLIB_KHR

#if defined(VK_USE_PLATFORM_DIRECTFB_EXT)
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDirectFBSurfaceEXT([[maybe_unused]] VkInstance instance,
                                                               [[maybe_unused]] const VkDirectFBSurfaceCreateInfoEXT* pCreateInfo,
                                                               const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    return VK_SUCCESS;
}

VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceDirectFBPresentationSupportEXT(VkPhysicalDevice, uint32_t, IDirectFB*) {
    return VK_TRUE;
}

#endif  // VK_USE_PLATFORM_DIRECTFB_EXT

#if defined(VK_USE_PLATFORM_MACOS_MVK)
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateMacOSSurfaceMVK([[maybe_unused]] VkInstance instance,
                                                            [[maybe_unused]] const VkMacOSSurfaceCreateInfoMVK* pCreateInfo,
                                                            const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    return VK_SUCCESS;
}
#endif  // VK_USE_PLATFORM_MACOS_MVK

#if defined(VK_USE_PLATFORM_IOS_MVK)
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateIOSSurfaceMVK([[maybe_unused]] VkInstance instance,
                                                          [[maybe_unused]] const VkIOSSurfaceCreateInfoMVK* pCreateInfo,
                                                          const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    return VK_SUCCESS;
}
#endif  // VK_USE_PLATFORM_IOS_MVK

#if defined(VK_USE_PLATFORM_GGP)
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateStreamDescriptorSurfaceGGP(
    [[maybe_unused]] VkInstance instance, [[maybe_unused]] const VkStreamDescriptorSurfaceCreateInfoGGP* pCreateInfo,
    const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    return VK_SUCCESS;
}
#endif  // VK_USE_PLATFORM_GGP

#if defined(VK_USE_PLATFORM_METAL_EXT)
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateMetalSurfaceEXT([[maybe_unused]] VkInstance instance,
                                                            [[maybe_unused]] const VkMetalSurfaceCreateInfoEXT* pCreateInfo,
                                                            const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    return VK_SUCCESS;
}
#endif  // VK_USE_PLATFORM_METAL_EXT

#if defined(VK_USE_PLATFORM_SCREEN_QNX)
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateScreenSurfaceQNX([[maybe_unused]] VkInstance instance,
                                                             [[maybe_unused]] const VkScreenSurfaceCreateInfoQNX* pCreateInfo,
                                                             const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    return VK_SUCCESS;
}

VKAPI_ATTR VkBool32 VKAPI_CALL test_vkGetPhysicalDeviceScreenPresentationSupportQNX(VkPhysicalDevice, uint32_t,
                                                                                    struct _screen_window*) {
    return VK_TRUE;
}
#endif  // VK_USE_PLATFORM_SCREEN_QNX

VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateHeadlessSurfaceEXT([[maybe_unused]] VkInstance instance,
                                                               [[maybe_unused]] const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo,
                                                               const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    if (IsInstanceExtensionSupported(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME)) {
        common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    }
    return VK_SUCCESS;
}

VKAPI_ATTR void VKAPI_CALL test_vkDestroySurfaceKHR([[maybe_unused]] VkInstance instance, VkSurfaceKHR surface,
                                                    const VkAllocationCallbacks* pAllocator) {
    if (surface != VK_NULL_HANDLE) {
        uint64_t surf_handle = from_nondispatch_handle(surface);
        auto found_iter = std::find(icd.surface_handles.begin(), icd.surface_handles.end(), surf_handle);
        if (found_iter != icd.surface_handles.end()) {
            // Remove it from the list
            icd.surface_handles.erase(found_iter);
        } else {
            assert(false && "Surface not found during destroy!");
        }
    }
}
// VK_KHR_swapchain
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateSwapchainKHR([[maybe_unused]] VkDevice device,
                                                         [[maybe_unused]] const VkSwapchainCreateInfoKHR* pCreateInfo,
                                                         const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) {
    check_allocator_handle(pAllocator);
    common_nondispatch_handle_creation(icd.swapchain_handles, pSwapchain);
    return VK_SUCCESS;
}

VKAPI_ATTR VkResult VKAPI_CALL test_vkGetSwapchainImagesKHR([[maybe_unused]] VkDevice device,
                                                            [[maybe_unused]] VkSwapchainKHR swapchain,
                                                            uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) {
    std::vector<uint64_t> handles{123, 234, 345, 345, 456};
    if (pSwapchainImages == nullptr) {
        if (pSwapchainImageCount) *pSwapchainImageCount = static_cast<uint32_t>(handles.size());
    } else if (pSwapchainImageCount) {
        for (uint32_t i = 0; i < *pSwapchainImageCount && i < handles.size(); i++) {
            pSwapchainImages[i] = to_nondispatch_handle<VkImage>(handles.back());
        }
        if (*pSwapchainImageCount < handles.size()) return VK_INCOMPLETE;
    }
    return VK_SUCCESS;
}

VKAPI_ATTR void VKAPI_CALL test_vkDestroySwapchainKHR([[maybe_unused]] VkDevice device, VkSwapchainKHR swapchain,
                                                      const VkAllocationCallbacks* pAllocator) {
    check_allocator_handle(pAllocator);
    if (swapchain != VK_NULL_HANDLE) {
        uint64_t fake_swapchain_handle = from_nondispatch_handle(swapchain);
        auto found_iter = icd.swapchain_handles.erase(
            std::remove(icd.swapchain_handles.begin(), icd.swapchain_handles.end(), fake_swapchain_handle),
            icd.swapchain_handles.end());
        if (!icd.swapchain_handles.empty() && found_iter == icd.swapchain_handles.end()) {
            assert(false && "Swapchain not found during destroy!");
        }
    }
}
// VK_KHR_swapchain with 1.1
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetDeviceGroupSurfacePresentModesKHR([[maybe_unused]] VkDevice device,
                                                                           [[maybe_unused]] VkSurfaceKHR surface,
                                                                           VkDeviceGroupPresentModeFlagsKHR* pModes) {
    if (!pModes) return VK_ERROR_INITIALIZATION_FAILED;
    *pModes = VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHR;
    return VK_SUCCESS;
}

// VK_KHR_surface
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex,
                                                                         VkSurfaceKHR surface, VkBool32* pSupported) {
    if (surface != VK_NULL_HANDLE) {
        if (!is_valid_surface(surface)) {
            *pSupported = VK_FALSE;
            return VK_SUCCESS;
        }
    }
    if (nullptr != pSupported) {
        if (icd.physical_devices.count(physicalDevice) > 0) {
            *pSupported = icd.physical_devices.at(physicalDevice).queue_family_properties.at(queueFamilyIndex).support_present;
        } else {
            *pSupported = VK_FALSE;
            return VK_SUCCESS;
        }
    }
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
                                                                              VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) {
    if (surface != VK_NULL_HANDLE) {
        if (!is_valid_surface(surface)) {
            assert(false && "Surface not found during GetPhysicalDeviceSurfaceCapabilitiesKHR query!");
            return VK_ERROR_UNKNOWN;
        }
    }
    if (nullptr != pSurfaceCapabilities) {
        *pSurfaceCapabilities = GetPhysDevice(physicalDevice).surface_capabilities;
    }
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
                                                                         uint32_t* pSurfaceFormatCount,
                                                                         VkSurfaceFormatKHR* pSurfaceFormats) {
    if (surface != VK_NULL_HANDLE) {
        if (!is_valid_surface(surface)) {
            assert(false && "Surface not found during GetPhysicalDeviceSurfaceFormatsKHR query!");
            return VK_ERROR_UNKNOWN;
        }
    } else {
        if (!IsInstanceExtensionEnabled(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME)) {
            assert(false && "Surface is NULL but VK_GOOGLE_surfaceless_query was not enabled!");
            return VK_ERROR_UNKNOWN;
        }
    }
    FillCountPtr(GetPhysDevice(physicalDevice).surface_formats, pSurfaceFormatCount, pSurfaceFormats);
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
                                                                              uint32_t* pPresentModeCount,
                                                                              VkPresentModeKHR* pPresentModes) {
    if (surface != VK_NULL_HANDLE) {
        if (!is_valid_surface(surface)) {
            assert(false && "Surface not found during GetPhysicalDeviceSurfacePresentModesKHR query!");
            return VK_ERROR_UNKNOWN;
        }
    } else {
        if (!IsInstanceExtensionEnabled(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME)) {
            assert(false && "Surface is NULL but VK_GOOGLE_surfaceless_query was not enabled!");
            return VK_ERROR_UNKNOWN;
        }
    }
    FillCountPtr(GetPhysDevice(physicalDevice).surface_present_modes, pPresentModeCount, pPresentModes);
    return VK_SUCCESS;
}

#if defined(WIN32)
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfacePresentModes2EXT(VkPhysicalDevice physicalDevice,
                                                                               const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
                                                                               uint32_t* pPresentModeCount,
                                                                               VkPresentModeKHR* pPresentModes) {
    if (pSurfaceInfo->surface != VK_NULL_HANDLE) {
        if (!is_valid_surface(pSurfaceInfo->surface)) {
            assert(false && "Surface not found during GetPhysicalDeviceSurfacePresentModesKHR query!");
            return VK_ERROR_UNKNOWN;
        }
    } else {
        if (!IsInstanceExtensionEnabled(VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME)) {
            assert(false && "Surface is NULL but VK_GOOGLE_surfaceless_query was not enabled!");
            return VK_ERROR_UNKNOWN;
        }
    }
    FillCountPtr(GetPhysDevice(physicalDevice).surface_present_modes, pPresentModeCount, pPresentModes);
    return VK_SUCCESS;
}
#endif

// VK_KHR_display
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physicalDevice,
                                                                            uint32_t* pPropertyCount,
                                                                            VkDisplayPropertiesKHR* pProperties) {
    FillCountPtr(GetPhysDevice(physicalDevice).display_properties, pPropertyCount, pProperties);
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physicalDevice,
                                                                                 uint32_t* pPropertyCount,
                                                                                 VkDisplayPlanePropertiesKHR* pProperties) {
    FillCountPtr(GetPhysDevice(physicalDevice).display_plane_properties, pPropertyCount, pProperties);
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physicalDevice,
                                                                          [[maybe_unused]] uint32_t planeIndex,
                                                                          uint32_t* pDisplayCount, VkDisplayKHR* pDisplays) {
    FillCountPtr(GetPhysDevice(physicalDevice).displays, pDisplayCount, pDisplays);
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetDisplayModePropertiesKHR(VkPhysicalDevice physicalDevice,
                                                                  [[maybe_unused]] VkDisplayKHR display, uint32_t* pPropertyCount,
                                                                  VkDisplayModePropertiesKHR* pProperties) {
    FillCountPtr(GetPhysDevice(physicalDevice).display_mode_properties, pPropertyCount, pProperties);
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDisplayModeKHR(VkPhysicalDevice physicalDevice, [[maybe_unused]] VkDisplayKHR display,
                                                           [[maybe_unused]] const VkDisplayModeCreateInfoKHR* pCreateInfo,
                                                           const VkAllocationCallbacks* pAllocator, VkDisplayModeKHR* pMode) {
    check_allocator_handle(pAllocator);
    if (nullptr != pMode) {
        *pMode = GetPhysDevice(physicalDevice).display_mode;
    }
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physicalDevice,
                                                                     [[maybe_unused]] VkDisplayModeKHR mode,
                                                                     [[maybe_unused]] uint32_t planeIndex,
                                                                     VkDisplayPlaneCapabilitiesKHR* pCapabilities) {
    if (nullptr != pCapabilities) {
        *pCapabilities = GetPhysDevice(physicalDevice).display_plane_capabilities;
    }
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDisplayPlaneSurfaceKHR(
    [[maybe_unused]] VkInstance instance, [[maybe_unused]] const VkDisplaySurfaceCreateInfoKHR* pCreateInfo,
    const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
    check_allocator_handle(pAllocator);
    if (IsInstanceExtensionSupported(VK_KHR_DISPLAY_EXTENSION_NAME)) {
        common_nondispatch_handle_creation(icd.surface_handles, pSurface);
    }
    return VK_SUCCESS;
}

// VK_KHR_get_surface_capabilities2
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice physicalDevice,
                                                                               const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
                                                                               VkSurfaceCapabilities2KHR* pSurfaceCapabilities) {
    if (nullptr != pSurfaceInfo && nullptr != pSurfaceCapabilities) {
        if (IsInstanceExtensionSupported("VK_EXT_surface_maintenance1") &&
            IsInstanceExtensionEnabled("VK_EXT_surface_maintenance1")) {
            auto& phys_dev = GetPhysDevice(physicalDevice);
            void* pNext = pSurfaceCapabilities->pNext;
            while (pNext) {
                VkBaseOutStructure pNext_base_structure{};
                std::memcpy(&pNext_base_structure, pNext, sizeof(VkBaseInStructure));
                if (pNext_base_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT) {
                    // First must find the present mode that is being queried
                    VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAX_ENUM_KHR;
                    const void* pSurfaceInfo_pNext = pSurfaceInfo->pNext;
                    while (pSurfaceInfo_pNext) {
                        VkBaseInStructure pSurfaceInfo_pNext_base_structure{};
                        std::memcpy(&pSurfaceInfo_pNext_base_structure, pSurfaceInfo_pNext, sizeof(VkBaseInStructure));
                        if (pSurfaceInfo_pNext_base_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT) {
                            present_mode = reinterpret_cast<const VkSurfacePresentModeEXT*>(pSurfaceInfo_pNext)->presentMode;
                        }
                        pSurfaceInfo_pNext = pSurfaceInfo_pNext_base_structure.pNext;
                    }

                    VkSurfacePresentModeCompatibilityEXT* present_mode_compatibility =
                        reinterpret_cast<VkSurfacePresentModeCompatibilityEXT*>(pNext);
                    if (present_mode == VK_PRESENT_MODE_MAX_ENUM_KHR) {
                        present_mode_compatibility->presentModeCount = 0;
                    } else {
                        auto it =
                            std::find(phys_dev.surface_present_modes.begin(), phys_dev.surface_present_modes.end(), present_mode);
                        if (it != phys_dev.surface_present_modes.end()) {
                            size_t index = it - phys_dev.surface_present_modes.begin();
                            present_mode_compatibility->presentModeCount =
                                static_cast<uint32_t>(phys_dev.surface_present_mode_compatibility[index].size());
                            if (present_mode_compatibility->pPresentModes) {
                                for (size_t i = 0; i < phys_dev.surface_present_mode_compatibility[index].size(); i++) {
                                    present_mode_compatibility->pPresentModes[i] =
                                        phys_dev.surface_present_mode_compatibility[index][i];
                                }
                            }
                        }
                    }
                } else if (pNext_base_structure.sType == VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT) {
                    VkSurfacePresentScalingCapabilitiesEXT* present_scaling_capabilities =
                        reinterpret_cast<VkSurfacePresentScalingCapabilitiesEXT*>(pNext);
                    present_scaling_capabilities->minScaledImageExtent =
                        phys_dev.surface_present_scaling_capabilities.minScaledImageExtent;
                    present_scaling_capabilities->maxScaledImageExtent =
                        phys_dev.surface_present_scaling_capabilities.maxScaledImageExtent;
                    present_scaling_capabilities->supportedPresentScaling =
                        phys_dev.surface_present_scaling_capabilities.supportedPresentScaling;
                    present_scaling_capabilities->supportedPresentGravityX =
                        phys_dev.surface_present_scaling_capabilities.supportedPresentGravityX;
                    present_scaling_capabilities->supportedPresentGravityY =
                        phys_dev.surface_present_scaling_capabilities.supportedPresentGravityY;
                }
                pNext = pNext_base_structure.pNext;
            }
        }
        return test_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, pSurfaceInfo->surface,
                                                              &pSurfaceCapabilities->surfaceCapabilities);
    }
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceSurfaceFormats2KHR(VkPhysicalDevice physicalDevice,
                                                                          const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
                                                                          uint32_t* pSurfaceFormatCount,
                                                                          VkSurfaceFormat2KHR* pSurfaceFormats) {
    if (nullptr != pSurfaceFormatCount) {
        test_vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount, nullptr);
        if (nullptr != pSurfaceFormats) {
            auto& phys_dev = GetPhysDevice(physicalDevice);
            // Since the structures are different, we have to copy each item over seperately.  Since we have multiple, we
            // have to manually copy the data here instead of calling the next function
            for (uint32_t cnt = 0; cnt < *pSurfaceFormatCount; ++cnt) {
                memcpy(&pSurfaceFormats[cnt].surfaceFormat, &phys_dev.surface_formats[cnt], sizeof(VkSurfaceFormatKHR));
            }
        }
    }
    return VK_SUCCESS;
}
// VK_KHR_display_swapchain
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateSharedSwapchainsKHR([[maybe_unused]] VkDevice device, uint32_t swapchainCount,
                                                                const VkSwapchainCreateInfoKHR* pCreateInfos,
                                                                const VkAllocationCallbacks* pAllocator,
                                                                VkSwapchainKHR* pSwapchains) {
    check_allocator_handle(pAllocator);
    for (uint32_t i = 0; i < swapchainCount; i++) {
        uint64_t surface_integer_value = from_nondispatch_handle(pCreateInfos[i].surface);
        auto found_iter = std::find(icd.surface_handles.begin(), icd.surface_handles.end(), surface_integer_value);
        if (found_iter == icd.surface_handles.end()) {
            return VK_ERROR_INITIALIZATION_FAILED;
        }
        common_nondispatch_handle_creation(icd.swapchain_handles, &pSwapchains[i]);
    }
    return VK_SUCCESS;
}

//// misc
VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateCommandPool([[maybe_unused]] VkDevice device,
                                                        [[maybe_unused]] const VkCommandPoolCreateInfo* pCreateInfo,
                                                        const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool) {
    check_allocator_handle(pAllocator);
    if (pCommandPool != nullptr) {
        pCommandPool = reinterpret_cast<VkCommandPool*>(0xdeadbeefdeadbeef);
    }
    return VK_SUCCESS;
}

VKAPI_ATTR void VKAPI_CALL test_vkDestroyCommandPool(VkDevice, VkCommandPool, const VkAllocationCallbacks*) {
    // do nothing, leak memory for now
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkAllocateCommandBuffers([[maybe_unused]] VkDevice device,
                                                             const VkCommandBufferAllocateInfo* pAllocateInfo,
                                                             VkCommandBuffer* pCommandBuffers) {
    if (pAllocateInfo != nullptr && pCommandBuffers != nullptr) {
        for (size_t i = 0; i < pAllocateInfo->commandBufferCount; i++) {
            icd.allocated_command_buffers.push_back({});
            pCommandBuffers[i] = icd.allocated_command_buffers.back().handle;
        }
    }
    return VK_SUCCESS;
}

VKAPI_ATTR void VKAPI_CALL test_vkGetDeviceQueue([[maybe_unused]] VkDevice device, [[maybe_unused]] uint32_t queueFamilyIndex,
                                                 uint32_t queueIndex, VkQueue* pQueue) {
    auto fd = lookup_device(device);
    if (fd.found) {
        *pQueue = icd.physical_devices.at(fd.phys_dev).queue_handles[queueIndex].handle;
    }
}

// VK_EXT_acquire_drm_display
VKAPI_ATTR VkResult VKAPI_CALL test_vkAcquireDrmDisplayEXT(VkPhysicalDevice, int32_t, VkDisplayKHR) { return VK_SUCCESS; }

VKAPI_ATTR VkResult VKAPI_CALL test_vkGetDrmDisplayEXT(VkPhysicalDevice physicalDevice, [[maybe_unused]] int32_t drmFd,
                                                       [[maybe_unused]] uint32_t connectorId, VkDisplayKHR* display) {
    if (nullptr != display && GetPhysDevice(physicalDevice).displays.size() > 0) {
        *display = GetPhysDevice(physicalDevice).displays[0];
    }
    return VK_SUCCESS;
}

//// stubs
// 1.0
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures) {
    if (nullptr != pFeatures) {
        memcpy(pFeatures, &GetPhysDevice(physicalDevice).features, sizeof(VkPhysicalDeviceFeatures));
    }
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice,
                                                              VkPhysicalDeviceProperties* pProperties) {
    if (nullptr != pProperties) {
        auto& phys_dev = GetPhysDevice(physicalDevice);
        memcpy(pProperties, &phys_dev.properties, sizeof(VkPhysicalDeviceProperties));
        size_t max_len = (phys_dev.deviceName.length() > VK_MAX_PHYSICAL_DEVICE_NAME_SIZE) ? VK_MAX_PHYSICAL_DEVICE_NAME_SIZE
                                                                                           : phys_dev.deviceName.length();
        std::copy(phys_dev.deviceName.c_str(), phys_dev.deviceName.c_str() + max_len, pProperties->deviceName);
        pProperties->deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE - 1] = '\0';
    }
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice,
                                                                    VkPhysicalDeviceMemoryProperties* pMemoryProperties) {
    if (nullptr != pMemoryProperties) {
        memcpy(pMemoryProperties, &GetPhysDevice(physicalDevice).memory_properties, sizeof(VkPhysicalDeviceMemoryProperties));
    }
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceSparseImageFormatProperties(
    VkPhysicalDevice physicalDevice, [[maybe_unused]] VkFormat format, [[maybe_unused]] VkImageType type,
    [[maybe_unused]] VkSampleCountFlagBits samples, [[maybe_unused]] VkImageUsageFlags usage, [[maybe_unused]] VkImageTiling tiling,
    uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties) {
    FillCountPtr(GetPhysDevice(physicalDevice).sparse_image_format_properties, pPropertyCount, pProperties);
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format,
                                                                    VkFormatProperties* pFormatProperties) {
    if (nullptr != pFormatProperties) {
        memcpy(pFormatProperties, &GetPhysDevice(physicalDevice).format_properties[static_cast<uint32_t>(format)],
               sizeof(VkFormatProperties));
    }
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceImageFormatProperties(
    VkPhysicalDevice physicalDevice, [[maybe_unused]] VkFormat format, [[maybe_unused]] VkImageType type,
    [[maybe_unused]] VkImageTiling tiling, [[maybe_unused]] VkImageUsageFlags usage, [[maybe_unused]] VkImageCreateFlags flags,
    VkImageFormatProperties* pImageFormatProperties) {
    if (nullptr != pImageFormatProperties) {
        memcpy(pImageFormatProperties, &GetPhysDevice(physicalDevice).image_format_properties, sizeof(VkImageFormatProperties));
    }
    return VK_SUCCESS;
}

// 1.1
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice,
                                                             VkPhysicalDeviceFeatures2* pFeatures) {
    if (nullptr != pFeatures) {
        test_vkGetPhysicalDeviceFeatures(physicalDevice, &pFeatures->features);
    }
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
                                                               VkPhysicalDeviceProperties2* pProperties) {
    if (nullptr != pProperties) {
        auto& phys_dev = GetPhysDevice(physicalDevice);
        test_vkGetPhysicalDeviceProperties(physicalDevice, &pProperties->properties);
        VkBaseInStructure* pNext = reinterpret_cast<VkBaseInStructure*>(pProperties->pNext);
        while (pNext) {
            if (pNext->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT) {
                auto* bus_info = reinterpret_cast<VkPhysicalDevicePCIBusInfoPropertiesEXT*>(pNext);
                bus_info->pciBus = phys_dev.pci_bus;
            }
            if (pNext->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LAYERED_DRIVER_PROPERTIES_MSFT) {
                auto* layered_driver_props = reinterpret_cast<VkPhysicalDeviceLayeredDriverPropertiesMSFT*>(pNext);
                layered_driver_props->underlyingAPI = phys_dev.layered_driver_underlying_api;
            }
            if (pNext->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES) {
                auto* vulkan_11_props = reinterpret_cast<VkPhysicalDeviceVulkan11Properties*>(pNext);
                memcpy(vulkan_11_props->deviceUUID, phys_dev.deviceUUID.data(), VK_UUID_SIZE);
                memcpy(vulkan_11_props->driverUUID, phys_dev.driverUUID.data(), VK_UUID_SIZE);
            }
            if (pNext->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES) {
                auto* device_id_props = reinterpret_cast<VkPhysicalDeviceIDProperties*>(pNext);
                memcpy(device_id_props->deviceUUID, phys_dev.deviceUUID.data(), VK_UUID_SIZE);
                memcpy(device_id_props->driverUUID, phys_dev.driverUUID.data(), VK_UUID_SIZE);
            }
            if (pNext->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES) {
                auto* device_driver_props = reinterpret_cast<VkPhysicalDeviceDriverProperties*>(pNext);
                *device_driver_props = phys_dev.driver_properties;
            }
            pNext = reinterpret_cast<VkBaseInStructure*>(const_cast<VkBaseInStructure*>(pNext->pNext));
        }
    }
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice,
                                                                     VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
    if (nullptr != pMemoryProperties) {
        test_vkGetPhysicalDeviceMemoryProperties(physicalDevice, &pMemoryProperties->memoryProperties);
    }
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice,
                                                                          uint32_t* pQueueFamilyPropertyCount,
                                                                          VkQueueFamilyProperties2* pQueueFamilyProperties) {
    if (nullptr != pQueueFamilyPropertyCount) {
        test_vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, nullptr);
        if (nullptr != pQueueFamilyProperties) {
            auto& phys_dev = GetPhysDevice(physicalDevice);
            // Since the structures are different, we have to copy each item over seperately.  Since we have multiple, we
            // have to manually copy the data here instead of calling the next function
            for (uint32_t queue = 0; queue < *pQueueFamilyPropertyCount; ++queue) {
                memcpy(&pQueueFamilyProperties[queue].queueFamilyProperties, &phys_dev.queue_family_properties[queue].properties,
                       sizeof(VkQueueFamilyProperties));
            }
        }
    }
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceSparseImageFormatProperties2(
    VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount,
    VkSparseImageFormatProperties2* pProperties) {
    if (nullptr != pPropertyCount) {
        test_vkGetPhysicalDeviceSparseImageFormatProperties(physicalDevice, pFormatInfo->format, pFormatInfo->type,
                                                            pFormatInfo->samples, pFormatInfo->usage, pFormatInfo->tiling,
                                                            pPropertyCount, nullptr);
        if (nullptr != pProperties) {
            auto& phys_dev = GetPhysDevice(physicalDevice);
            // Since the structures are different, we have to copy each item over seperately.  Since we have multiple, we
            // have to manually copy the data here instead of calling the next function
            for (uint32_t cnt = 0; cnt < *pPropertyCount; ++cnt) {
                memcpy(&pProperties[cnt].properties, &phys_dev.sparse_image_format_properties[cnt],
                       sizeof(VkSparseImageFormatProperties));
            }
        }
    }
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format,
                                                                     VkFormatProperties2* pFormatProperties) {
    if (nullptr != pFormatProperties) {
        test_vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &pFormatProperties->formatProperties);
    }
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceImageFormatProperties2(
    VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
    VkImageFormatProperties2* pImageFormatProperties) {
    if (nullptr != pImageFormatInfo) {
        VkImageFormatProperties* ptr = nullptr;
        if (pImageFormatProperties) {
            ptr = &pImageFormatProperties->imageFormatProperties;
        }
        test_vkGetPhysicalDeviceImageFormatProperties(physicalDevice, pImageFormatInfo->format, pImageFormatInfo->type,
                                                      pImageFormatInfo->tiling, pImageFormatInfo->usage, pImageFormatInfo->flags,
                                                      ptr);
    }
    return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceExternalBufferProperties(
    VkPhysicalDevice physicalDevice, [[maybe_unused]] const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
    VkExternalBufferProperties* pExternalBufferProperties) {
    if (nullptr != pExternalBufferProperties) {
        auto& phys_dev = GetPhysDevice(physicalDevice);
        memcpy(&pExternalBufferProperties->externalMemoryProperties, &phys_dev.external_memory_properties,
               sizeof(VkExternalMemoryProperties));
    }
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceExternalSemaphoreProperties(
    VkPhysicalDevice physicalDevice, [[maybe_unused]] const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
    VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
    if (nullptr != pExternalSemaphoreProperties) {
        auto& phys_dev = GetPhysDevice(physicalDevice);
        memcpy(pExternalSemaphoreProperties, &phys_dev.external_semaphore_properties, sizeof(VkExternalSemaphoreProperties));
    }
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceExternalFenceProperties(
    VkPhysicalDevice physicalDevice, [[maybe_unused]] const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
    VkExternalFenceProperties* pExternalFenceProperties) {
    if (nullptr != pExternalFenceProperties) {
        auto& phys_dev = GetPhysDevice(physicalDevice);
        memcpy(pExternalFenceProperties, &phys_dev.external_fence_properties, sizeof(VkExternalFenceProperties));
    }
}
// Entry-points associated with the VK_KHR_performance_query extension
VKAPI_ATTR VkResult VKAPI_CALL test_vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR(
    VkPhysicalDevice, uint32_t, uint32_t*, VkPerformanceCounterKHR*, VkPerformanceCounterDescriptionKHR*) {
    return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR(VkPhysicalDevice,
                                                                                        const VkQueryPoolPerformanceCreateInfoKHR*,
                                                                                        uint32_t*) {}
VKAPI_ATTR VkResult VKAPI_CALL test_vkAcquireProfilingLockKHR(VkDevice, const VkAcquireProfilingLockInfoKHR*) { return VK_SUCCESS; }
VKAPI_ATTR void VKAPI_CALL test_vkReleaseProfilingLockKHR(VkDevice) {}
// Entry-points associated with the VK_EXT_sample_locations extension
VKAPI_ATTR void VKAPI_CALL test_vkCmdSetSampleLocationsEXT(VkCommandBuffer, const VkSampleLocationsInfoEXT*) {}
VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceMultisamplePropertiesEXT(VkPhysicalDevice, VkSampleCountFlagBits,
                                                                            VkMultisamplePropertiesEXT*) {}
// Entry-points associated with the VK_EXT_calibrated_timestamps extension
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(VkPhysicalDevice, uint32_t*, VkTimeDomainEXT*) {
    return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL test_vkGetCalibratedTimestampsEXT(VkDevice, uint32_t, const VkCalibratedTimestampInfoEXT*, uint64_t*,
                                                                 uint64_t*) {
    return VK_SUCCESS;
}

#if defined(WIN32)
VKAPI_ATTR VkResult VKAPI_CALL test_vk_icdEnumerateAdapterPhysicalDevices(VkInstance instance, LUID adapterLUID,
                                                                          uint32_t* pPhysicalDeviceCount,
                                                                          VkPhysicalDevice* pPhysicalDevices) {
    if (icd.enum_adapter_physical_devices_return_code != VK_SUCCESS) {
        return icd.enum_adapter_physical_devices_return_code;
    }
    if (adapterLUID.LowPart != icd.adapterLUID.LowPart || adapterLUID.HighPart != icd.adapterLUID.HighPart) {
        *pPhysicalDeviceCount = 0;
        return VK_ERROR_INCOMPATIBLE_DRIVER;
    }
    icd.called_enumerate_adapter_physical_devices = true;
    VkResult res = test_vkEnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
    // For this testing, flip order intentionally
    if (nullptr != pPhysicalDevices) {
        std::reverse(pPhysicalDevices, pPhysicalDevices + *pPhysicalDeviceCount);
    }
    return res;
}
#endif  // defined(WIN32)

VKAPI_ATTR VkResult VKAPI_CALL test_vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion) {
    if (icd.called_vk_icd_gipa == CalledICDGIPA::not_called &&
        icd.called_negotiate_interface == CalledNegotiateInterface::not_called)
        icd.called_negotiate_interface = CalledNegotiateInterface::vk_icd_negotiate;
    else if (icd.called_vk_icd_gipa != CalledICDGIPA::not_called)
        icd.called_negotiate_interface = CalledNegotiateInterface::vk_icd_gipa_first;

    // loader puts the minimum it supports in pSupportedVersion, if that is lower than our minimum
    // If the ICD doesn't supports the interface version provided by the loader, report VK_ERROR_INCOMPATIBLE_DRIVER
    if (icd.min_icd_interface_version > *pSupportedVersion) {
        icd.interface_version_check = InterfaceVersionCheck::loader_version_too_old;
        *pSupportedVersion = icd.min_icd_interface_version;
        return VK_ERROR_INCOMPATIBLE_DRIVER;
    }

    // the loader-provided interface version is newer than that supported by the ICD
    if (icd.max_icd_interface_version < *pSupportedVersion) {
        icd.interface_version_check = InterfaceVersionCheck::loader_version_too_new;
        *pSupportedVersion = icd.max_icd_interface_version;
    }
    // ICD interface version is greater than the loader's,  return the loader's version
    else if (icd.max_icd_interface_version > *pSupportedVersion) {
        icd.interface_version_check = InterfaceVersionCheck::icd_version_too_new;
        // don't change *pSupportedVersion
    } else {
        icd.interface_version_check = InterfaceVersionCheck::version_is_supported;
        *pSupportedVersion = icd.max_icd_interface_version;
    }

    return VK_SUCCESS;
}

//// trampolines

PFN_vkVoidFunction get_instance_func_ver_1_1([[maybe_unused]] VkInstance instance, const char* pName) {
    if (icd.icd_api_version >= VK_API_VERSION_1_1) {
        if (string_eq(pName, "test_vkEnumerateInstanceVersion")) {
            return icd.can_query_vkEnumerateInstanceVersion ? to_vkVoidFunction(test_vkEnumerateInstanceVersion) : nullptr;
        }
        if (string_eq(pName, "vkEnumeratePhysicalDeviceGroups")) {
            return to_vkVoidFunction(test_vkEnumeratePhysicalDeviceGroups);
        }
    }
    return nullptr;
}

PFN_vkVoidFunction get_physical_device_func_wsi([[maybe_unused]] VkInstance instance, const char* pName) {
    if (IsInstanceExtensionEnabled("VK_KHR_surface")) {
        if (string_eq(pName, "vkGetPhysicalDeviceSurfaceSupportKHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceSurfaceSupportKHR);
        if (string_eq(pName, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
        if (string_eq(pName, "vkGetPhysicalDeviceSurfaceFormatsKHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceSurfaceFormatsKHR);
        if (string_eq(pName, "vkGetPhysicalDeviceSurfacePresentModesKHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceSurfacePresentModesKHR);
    }
#if defined(WIN32)
    if (IsPhysicalDeviceExtensionAvailable("VK_EXT_full_screen_exclusive")) {
        if (string_eq(pName, "vkGetPhysicalDeviceSurfacePresentModes2EXT"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceSurfacePresentModes2EXT);
    }
#endif
    if (IsInstanceExtensionEnabled("VK_KHR_get_surface_capabilities2")) {
        if (string_eq(pName, "vkGetPhysicalDeviceSurfaceCapabilities2KHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceSurfaceCapabilities2KHR);
        if (string_eq(pName, "vkGetPhysicalDeviceSurfaceFormats2KHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceSurfaceFormats2KHR);
    }
    if (IsInstanceExtensionEnabled("VK_KHR_display")) {
        if (string_eq(pName, "vkGetPhysicalDeviceDisplayPropertiesKHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceDisplayPropertiesKHR);
        if (string_eq(pName, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceDisplayPlanePropertiesKHR);
        if (string_eq(pName, "vkGetDisplayPlaneSupportedDisplaysKHR"))
            return to_vkVoidFunction(test_vkGetDisplayPlaneSupportedDisplaysKHR);
        if (string_eq(pName, "vkGetDisplayModePropertiesKHR")) return to_vkVoidFunction(test_vkGetDisplayModePropertiesKHR);
        if (string_eq(pName, "vkCreateDisplayModeKHR")) return to_vkVoidFunction(test_vkCreateDisplayModeKHR);
        if (string_eq(pName, "vkGetDisplayPlaneCapabilitiesKHR")) return to_vkVoidFunction(test_vkGetDisplayPlaneCapabilitiesKHR);
        if (string_eq(pName, "vkCreateDisplayPlaneSurfaceKHR")) return to_vkVoidFunction(test_vkCreateDisplayPlaneSurfaceKHR);
    }
    if (IsInstanceExtensionEnabled("VK_EXT_acquire_drm_display")) {
        if (string_eq(pName, "vkAcquireDrmDisplayEXT")) return to_vkVoidFunction(test_vkAcquireDrmDisplayEXT);
        if (string_eq(pName, "vkGetDrmDisplayEXT")) return to_vkVoidFunction(test_vkGetDrmDisplayEXT);
    }
    return nullptr;
}

PFN_vkVoidFunction get_instance_func_wsi(VkInstance instance, const char* pName) {
    if (icd.min_icd_interface_version >= 3 && icd.enable_icd_wsi == true) {
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
        if (string_eq(pName, "vkCreateAndroidSurfaceKHR")) {
            icd.is_using_icd_wsi = true;
            return to_vkVoidFunction(test_vkCreateAndroidSurfaceKHR);
        }
#endif
#if defined(VK_USE_PLATFORM_METAL_EXT)
        if (string_eq(pName, "vkCreateMetalSurfaceEXT")) {
            return to_vkVoidFunction(test_vkCreateMetalSurfaceEXT);
        }
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
        if (string_eq(pName, "vkCreateWaylandSurfaceKHR")) {
            return to_vkVoidFunction(test_vkCreateWaylandSurfaceKHR);
        }
        if (string_eq(pName, "vkGetPhysicalDeviceWaylandPresentationSupportKHR")) {
            return to_vkVoidFunction(test_vkGetPhysicalDeviceWaylandPresentationSupportKHR);
        }
#endif
#if defined(VK_USE_PLATFORM_XCB_KHR)
        if (string_eq(pName, "vkCreateXcbSurfaceKHR")) {
            return to_vkVoidFunction(test_vkCreateXcbSurfaceKHR);
        }
        if (string_eq(pName, "vkGetPhysicalDeviceXcbPresentationSupportKHR")) {
            return to_vkVoidFunction(test_vkGetPhysicalDeviceXcbPresentationSupportKHR);
        }
#endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
        if (string_eq(pName, "vkCreateXlibSurfaceKHR")) {
            return to_vkVoidFunction(test_vkCreateXlibSurfaceKHR);
        }
        if (string_eq(pName, "vkGetPhysicalDeviceXlibPresentationSupportKHR")) {
            return to_vkVoidFunction(test_vkGetPhysicalDeviceXlibPresentationSupportKHR);
        }
#endif
#if defined(VK_USE_PLATFORM_WIN32_KHR)
        if (string_eq(pName, "vkCreateWin32SurfaceKHR")) {
            return to_vkVoidFunction(test_vkCreateWin32SurfaceKHR);
        }
        if (string_eq(pName, "vkGetPhysicalDeviceWin32PresentationSupportKHR")) {
            return to_vkVoidFunction(test_vkGetPhysicalDeviceWin32PresentationSupportKHR);
        }
#endif
#if defined(VK_USE_PLATFORM_DIRECTFB_EXT)
        if (string_eq(pName, "vkCreateDirectFBSurfaceEXT")) {
            return to_vkVoidFunction(test_vkCreateDirectFBSurfaceEXT);
        }
        if (string_eq(pName, "vkGetPhysicalDeviceDirectFBPresentationSupportEXT")) {
            return to_vkVoidFunction(test_vkGetPhysicalDeviceDirectFBPresentationSupportEXT);
        }
#endif  // VK_USE_PLATFORM_DIRECTFB_EXT

#if defined(VK_USE_PLATFORM_MACOS_MVK)
        if (string_eq(pName, "vkCreateMacOSSurfaceMVK")) {
            return to_vkVoidFunction(test_vkCreateMacOSSurfaceMVK);
        }
#endif  // VK_USE_PLATFORM_MACOS_MVK

#if defined(VK_USE_PLATFORM_IOS_MVK)
        if (string_eq(pName, "vkCreateIOSSurfaceMVK")) {
            return to_vkVoidFunction(test_vkCreateIOSSurfaceMVK);
        }
#endif  // VK_USE_PLATFORM_IOS_MVK

#if defined(VK_USE_PLATFORM_GGP)
        if (string_eq(pName, "vkCreateStreamDescriptorSurfaceGGP")) {
            return to_vkVoidFunction(test_vkCreateStreamDescriptorSurfaceGGP);
        }
#endif  // VK_USE_PLATFORM_GGP

#if defined(VK_USE_PLATFORM_SCREEN_QNX)
        if (string_eq(pName, "vkCreateScreenSurfaceQNX")) {
            return to_vkVoidFunction(test_vkCreateScreenSurfaceQNX);
        }
        if (string_eq(pName, "vkGetPhysicalDeviceScreenPresentationSupportQNX")) {
            return to_vkVoidFunction(test_vkGetPhysicalDeviceScreenPresentationSupportQNX);
        }
#endif  // VK_USE_PLATFORM_SCREEN_QNX

        if (string_eq(pName, "vkCreateHeadlessSurfaceEXT")) {
            return to_vkVoidFunction(test_vkCreateHeadlessSurfaceEXT);
        }

        if (string_eq(pName, "vkDestroySurfaceKHR")) {
            icd.is_using_icd_wsi = true;
            return to_vkVoidFunction(test_vkDestroySurfaceKHR);
        }
    }
    if (IsInstanceExtensionEnabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) {
        if (string_eq(pName, "vkCreateDebugUtilsMessengerEXT")) {
            return to_vkVoidFunction(test_vkCreateDebugUtilsMessengerEXT);
        }
        if (string_eq(pName, "vkDestroyDebugUtilsMessengerEXT")) {
            return to_vkVoidFunction(test_vkDestroyDebugUtilsMessengerEXT);
        }
    }
    if (IsInstanceExtensionEnabled(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
        if (string_eq(pName, "vkCreateDebugReportCallbackEXT")) {
            return to_vkVoidFunction(test_vkCreateDebugReportCallbackEXT);
        }
        if (string_eq(pName, "vkDestroyDebugReportCallbackEXT")) {
            return to_vkVoidFunction(test_vkDestroyDebugReportCallbackEXT);
        }
    }

    PFN_vkVoidFunction ret_phys_dev_wsi = get_physical_device_func_wsi(instance, pName);
    if (ret_phys_dev_wsi != nullptr) return ret_phys_dev_wsi;
    return nullptr;
}
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_physical_device_func([[maybe_unused]] VkInstance instance, const char* pName) {
    if (string_eq(pName, "vkEnumerateDeviceLayerProperties")) return to_vkVoidFunction(test_vkEnumerateDeviceLayerProperties);
    if (string_eq(pName, "vkEnumerateDeviceExtensionProperties"))
        return to_vkVoidFunction(test_vkEnumerateDeviceExtensionProperties);
    if (string_eq(pName, "vkGetPhysicalDeviceQueueFamilyProperties"))
        return to_vkVoidFunction(test_vkGetPhysicalDeviceQueueFamilyProperties);
    if (string_eq(pName, "vkCreateDevice")) return to_vkVoidFunction(test_vkCreateDevice);

    if (string_eq(pName, "vkGetPhysicalDeviceFeatures"))
        return icd.can_query_GetPhysicalDeviceFuncs ? to_vkVoidFunction(test_vkGetPhysicalDeviceFeatures) : nullptr;
    if (string_eq(pName, "vkGetPhysicalDeviceProperties"))
        return icd.can_query_GetPhysicalDeviceFuncs ? to_vkVoidFunction(test_vkGetPhysicalDeviceProperties) : nullptr;
    if (string_eq(pName, "vkGetPhysicalDeviceMemoryProperties"))
        return icd.can_query_GetPhysicalDeviceFuncs ? to_vkVoidFunction(test_vkGetPhysicalDeviceMemoryProperties) : nullptr;
    if (string_eq(pName, "vkGetPhysicalDeviceSparseImageFormatProperties"))
        return icd.can_query_GetPhysicalDeviceFuncs ? to_vkVoidFunction(test_vkGetPhysicalDeviceSparseImageFormatProperties)
                                                    : nullptr;
    if (string_eq(pName, "vkGetPhysicalDeviceFormatProperties"))
        return icd.can_query_GetPhysicalDeviceFuncs ? to_vkVoidFunction(test_vkGetPhysicalDeviceFormatProperties) : nullptr;
    if (string_eq(pName, "vkGetPhysicalDeviceImageFormatProperties"))
        return icd.can_query_GetPhysicalDeviceFuncs ? to_vkVoidFunction(test_vkGetPhysicalDeviceImageFormatProperties) : nullptr;

    if (IsInstanceExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
        if (string_eq(pName, "vkGetPhysicalDeviceFeatures2KHR")) return to_vkVoidFunction(test_vkGetPhysicalDeviceFeatures2);
        if (string_eq(pName, "vkGetPhysicalDeviceProperties2KHR")) return to_vkVoidFunction(test_vkGetPhysicalDeviceProperties2);
        if (string_eq(pName, "vkGetPhysicalDeviceFormatProperties2KHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceFormatProperties2);
        if (string_eq(pName, "vkGetPhysicalDeviceMemoryProperties2KHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceMemoryProperties2);

        if (string_eq(pName, "vkGetPhysicalDeviceQueueFamilyProperties2KHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceQueueFamilyProperties2);

        if (string_eq(pName, "vkGetPhysicalDeviceSparseImageFormatProperties2KHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceSparseImageFormatProperties2);

        if (string_eq(pName, "vkGetPhysicalDeviceImageFormatProperties2KHR")) {
            return to_vkVoidFunction(test_vkGetPhysicalDeviceImageFormatProperties2);
        }
    }
    if (IsInstanceExtensionEnabled(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME)) {
        if (string_eq(pName, "vkGetPhysicalDeviceExternalBufferPropertiesKHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceExternalBufferProperties);
    }
    if (IsInstanceExtensionEnabled(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME)) {
        if (string_eq(pName, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceExternalSemaphoreProperties);
    }
    if (IsInstanceExtensionEnabled(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME)) {
        if (string_eq(pName, "vkGetPhysicalDeviceExternalFencePropertiesKHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceExternalFenceProperties);
    }

    // The following physical device extensions only need 1 device to support them for the ICD to export
    // them
    if (IsPhysicalDeviceExtensionAvailable(VK_KHR_PERFORMANCE_QUERY_EXTENSION_NAME)) {
        if (string_eq(pName, "vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR"))
            return to_vkVoidFunction(test_vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR);
        if (string_eq(pName, "vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR);
        if (string_eq(pName, "vkAcquireProfilingLockKHR")) return to_vkVoidFunction(test_vkAcquireProfilingLockKHR);
        if (string_eq(pName, "vkReleaseProfilingLockKHR")) return to_vkVoidFunction(test_vkReleaseProfilingLockKHR);
    }
    if (IsPhysicalDeviceExtensionAvailable(VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME)) {
        if (string_eq(pName, "vkCmdSetSampleLocationsEXT")) return to_vkVoidFunction(test_vkCmdSetSampleLocationsEXT);
        if (string_eq(pName, "vkGetPhysicalDeviceMultisamplePropertiesEXT"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceMultisamplePropertiesEXT);
    }
    if (IsPhysicalDeviceExtensionAvailable(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME)) {
        if (string_eq(pName, "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT);
        if (string_eq(pName, "vkGetCalibratedTimestampsEXT")) return to_vkVoidFunction(test_vkGetCalibratedTimestampsEXT);
    }

    if (icd.icd_api_version >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
        if (string_eq(pName, "vkGetPhysicalDeviceFeatures2")) return to_vkVoidFunction(test_vkGetPhysicalDeviceFeatures2);
        if (string_eq(pName, "vkGetPhysicalDeviceProperties2")) return to_vkVoidFunction(test_vkGetPhysicalDeviceProperties2);
        if (string_eq(pName, "vkGetPhysicalDeviceFormatProperties2"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceFormatProperties2);
        if (string_eq(pName, "vkGetPhysicalDeviceMemoryProperties2"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceMemoryProperties2);

        if (string_eq(pName, "vkGetPhysicalDeviceQueueFamilyProperties2"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceQueueFamilyProperties2);

        if (string_eq(pName, "vkGetPhysicalDeviceSparseImageFormatProperties2"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceSparseImageFormatProperties2);

        if (string_eq(pName, "vkGetPhysicalDeviceImageFormatProperties2")) {
            return to_vkVoidFunction(test_vkGetPhysicalDeviceImageFormatProperties2);
        }

        if (string_eq(pName, "vkGetPhysicalDeviceExternalBufferProperties"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceExternalBufferProperties);
        if (string_eq(pName, "vkGetPhysicalDeviceExternalSemaphoreProperties"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceExternalSemaphoreProperties);
        if (string_eq(pName, "vkGetPhysicalDeviceExternalFenceProperties"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceExternalFenceProperties);
    }

    if (icd.supports_tooling_info_core) {
        if (string_eq(pName, "vkGetPhysicalDeviceToolProperties")) return to_vkVoidFunction(test_vkGetPhysicalDeviceToolProperties);
    }
    if (icd.supports_tooling_info_ext) {
        if (string_eq(pName, "vkGetPhysicalDeviceToolPropertiesEXT"))
            return to_vkVoidFunction(test_vkGetPhysicalDeviceToolPropertiesEXT);
    }

    for (auto const& [phys_dev_handle, phys_dev] : icd.physical_devices) {
        for (auto& func : phys_dev.custom_physical_device_functions) {
            if (func.name == pName) {
                return to_vkVoidFunction(func.function);
            }
        }
    }
    return nullptr;
}

PFN_vkVoidFunction get_instance_func(VkInstance instance, const char* pName) {
    if (string_eq(pName, "vkDestroyInstance")) return to_vkVoidFunction(test_vkDestroyInstance);
    if (string_eq(pName, "vkEnumeratePhysicalDevices")) return to_vkVoidFunction(test_vkEnumeratePhysicalDevices);

    if (IsInstanceExtensionEnabled(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME)) {
        if (string_eq(pName, "vkEnumeratePhysicalDeviceGroupsKHR")) return to_vkVoidFunction(test_vkEnumeratePhysicalDeviceGroups);
    }

    PFN_vkVoidFunction ret_phys_dev = get_physical_device_func(instance, pName);
    if (ret_phys_dev != nullptr) return ret_phys_dev;

    PFN_vkVoidFunction ret_1_1 = get_instance_func_ver_1_1(instance, pName);
    if (ret_1_1 != nullptr) return ret_1_1;

    PFN_vkVoidFunction ret_wsi = get_instance_func_wsi(instance, pName);
    if (ret_wsi != nullptr) return ret_wsi;

    for (auto& func : icd.custom_instance_functions) {
        if (func.name == pName) {
            return to_vkVoidFunction(func.function);
        }
    }

    return nullptr;
}

bool should_check(std::vector<const char*>* exts, VkDevice device, const char* ext_name) {
    if (exts == nullptr || device == VK_NULL_HANDLE) return true;  // always look if device is NULL
    for (auto const& ext : *exts) {
        if (string_eq(ext, ext_name)) {
            return true;
        }
    }
    return false;
}

PFN_vkVoidFunction get_device_func(VkDevice device, const char* pName) {
    std::vector<const char*>* enabled_extensions = nullptr;
    FindDevice found_device{};
    if (device != nullptr) {
        found_device = lookup_device(device);
        if (!found_device.found) return NULL;
        enabled_extensions =
            &icd.physical_devices.at(found_device.phys_dev).device_create_infos.at(found_device.dev_index).enabled_extensions;
    }
    if (string_eq(pName, "vkCreateCommandPool")) return to_vkVoidFunction(test_vkCreateCommandPool);
    if (string_eq(pName, "vkAllocateCommandBuffers")) return to_vkVoidFunction(test_vkAllocateCommandBuffers);
    if (string_eq(pName, "vkDestroyCommandPool")) return to_vkVoidFunction(test_vkDestroyCommandPool);
    if (string_eq(pName, "vkGetDeviceQueue")) return to_vkVoidFunction(test_vkGetDeviceQueue);
    if (string_eq(pName, "vkDestroyDevice")) return to_vkVoidFunction(test_vkDestroyDevice);
    if (should_check(enabled_extensions, device, "VK_KHR_swapchain")) {
        if (string_eq(pName, "vkCreateSwapchainKHR")) return to_vkVoidFunction(test_vkCreateSwapchainKHR);
        if (string_eq(pName, "vkGetSwapchainImagesKHR")) return to_vkVoidFunction(test_vkGetSwapchainImagesKHR);
        if (string_eq(pName, "vkDestroySwapchainKHR")) return to_vkVoidFunction(test_vkDestroySwapchainKHR);

        if (icd.icd_api_version >= VK_API_VERSION_1_1 && string_eq(pName, "vkGetDeviceGroupSurfacePresentModesKHR"))
            return to_vkVoidFunction(test_vkGetDeviceGroupSurfacePresentModesKHR);
    }
    if (should_check(enabled_extensions, device, "VK_KHR_display_swapchain")) {
        if (string_eq(pName, "vkCreateSharedSwapchainsKHR")) return to_vkVoidFunction(test_vkCreateSharedSwapchainsKHR);
    }
    if (should_check(enabled_extensions, device, "VK_KHR_device_group")) {
        if (string_eq(pName, "vkGetDeviceGroupSurfacePresentModesKHR"))
            return to_vkVoidFunction(test_vkGetDeviceGroupSurfacePresentModesKHR);
    }
    if (should_check(enabled_extensions, device, "VK_EXT_debug_marker")) {
        if (string_eq(pName, "vkDebugMarkerSetObjectTagEXT")) return to_vkVoidFunction(test_vkDebugMarkerSetObjectTagEXT);
        if (string_eq(pName, "vkDebugMarkerSetObjectNameEXT")) return to_vkVoidFunction(test_vkDebugMarkerSetObjectNameEXT);
        if (string_eq(pName, "vkCmdDebugMarkerBeginEXT")) return to_vkVoidFunction(test_vkCmdDebugMarkerBeginEXT);
        if (string_eq(pName, "vkCmdDebugMarkerEndEXT")) return to_vkVoidFunction(test_vkCmdDebugMarkerEndEXT);
        if (string_eq(pName, "vkCmdDebugMarkerInsertEXT")) return to_vkVoidFunction(test_vkCmdDebugMarkerInsertEXT);
    }
    if (IsInstanceExtensionEnabled("VK_EXT_debug_utils")) {
        if (string_eq(pName, "vkSetDebugUtilsObjectNameEXT")) return to_vkVoidFunction(test_vkSetDebugUtilsObjectNameEXT);
        if (string_eq(pName, "vkSetDebugUtilsObjectTagEXT")) return to_vkVoidFunction(test_vkSetDebugUtilsObjectTagEXT);
        if (string_eq(pName, "vkQueueBeginDebugUtilsLabelEXT")) return to_vkVoidFunction(test_vkQueueBeginDebugUtilsLabelEXT);
        if (string_eq(pName, "vkQueueEndDebugUtilsLabelEXT")) return to_vkVoidFunction(test_vkQueueEndDebugUtilsLabelEXT);
        if (string_eq(pName, "vkQueueInsertDebugUtilsLabelEXT")) return to_vkVoidFunction(test_vkQueueInsertDebugUtilsLabelEXT);
        if (string_eq(pName, "vkCmdBeginDebugUtilsLabelEXT")) return to_vkVoidFunction(test_vkCmdBeginDebugUtilsLabelEXT);
        if (string_eq(pName, "vkCmdEndDebugUtilsLabelEXT")) return to_vkVoidFunction(test_vkCmdEndDebugUtilsLabelEXT);
        if (string_eq(pName, "vkCmdInsertDebugUtilsLabelEXT")) return to_vkVoidFunction(test_vkCmdInsertDebugUtilsLabelEXT);
    }
    if (found_device.found) {
        // look for device functions setup from a test
        for (const auto& function : icd.physical_devices.at(found_device.phys_dev).known_device_functions) {
            if (function.name == pName) {
                return to_vkVoidFunction(function.function);
            }
        }
    } else {
        for (const auto& [handle, phys_dev] : icd.physical_devices) {
            for (const auto& function : phys_dev.known_device_functions) {
                if (function.name == pName) {
                    return to_vkVoidFunction(function.function);
                }
            }
        }
    }
    return nullptr;
}

VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL test_vkGetInstanceProcAddr(VkInstance instance, const char* pName) {
    return get_instance_func(instance, pName);
}

VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL test_vkGetDeviceProcAddr(VkDevice device, const char* pName) {
    return get_device_func(device, pName);
}

PFN_vkVoidFunction base_get_instance_proc_addr(VkInstance instance, const char* pName) {
    if (pName == nullptr) return nullptr;
    if (instance == NULL) {
        if (string_eq(pName, "vk_icdNegotiateLoaderICDInterfaceVersion"))
            return icd.exposes_vk_icdNegotiateLoaderICDInterfaceVersion
                       ? to_vkVoidFunction(test_vk_icdNegotiateLoaderICDInterfaceVersion)
                       : NULL;

        if (string_eq(pName, "vk_icdGetPhysicalDeviceProcAddr"))
            return icd.exposes_vk_icdGetPhysicalDeviceProcAddr ? to_vkVoidFunction(get_physical_device_func) : NULL;
#if defined(WIN32)
        if (string_eq(pName, "vk_icdEnumerateAdapterPhysicalDevices"))
            return icd.exposes_vk_icdEnumerateAdapterPhysicalDevices ? to_vkVoidFunction(test_vk_icdEnumerateAdapterPhysicalDevices)
                                                                     : NULL;
#endif  // defined(WIN32)

        if (string_eq(pName, "vkGetInstanceProcAddr")) return to_vkVoidFunction(test_vkGetInstanceProcAddr);
        if (string_eq(pName, "vkEnumerateInstanceExtensionProperties"))
            return icd.exposes_vkEnumerateInstanceExtensionProperties
                       ? to_vkVoidFunction(test_vkEnumerateInstanceExtensionProperties)
                       : NULL;
        if (string_eq(pName, "vkEnumerateInstanceLayerProperties"))
            return to_vkVoidFunction(test_vkEnumerateInstanceLayerProperties);
        if (string_eq(pName, "vkEnumerateInstanceVersion"))
            return icd.can_query_vkEnumerateInstanceVersion ? to_vkVoidFunction(test_vkEnumerateInstanceVersion) : nullptr;
        if (string_eq(pName, "vkCreateInstance")) return to_vkVoidFunction(test_vkCreateInstance);
    }
    if (string_eq(pName, "vkGetDeviceProcAddr")) return to_vkVoidFunction(test_vkGetDeviceProcAddr);

    auto instance_func_return = get_instance_func(instance, pName);
    if (instance_func_return != nullptr) return instance_func_return;

    // Need to return function pointers for device extensions
    auto device_func_return = get_device_func(nullptr, pName);
    if (device_func_return != nullptr) return device_func_return;
    return nullptr;
}

// Exported functions
extern "C" {
#if TEST_ICD_EXPORT_NEGOTIATE_INTERFACE_VERSION && TEST_ICD_EXPORT_VERSION_7
FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion) {
    return test_vk_icdNegotiateLoaderICDInterfaceVersion(pSupportedVersion);
}
#endif  // TEST_ICD_EXPORT_NEGOTIATE_INTERFACE_VERSION

#if TEST_ICD_EXPORT_ICD_GPDPA && TEST_ICD_EXPORT_VERSION_7
FRAMEWORK_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetPhysicalDeviceProcAddr(VkInstance instance, const char* pName) {
    return get_physical_device_func(instance, pName);
}
#endif  // TEST_ICD_EXPORT_ICD_GPDPA

#if TEST_ICD_EXPORT_ICD_GIPA
FRAMEWORK_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetInstanceProcAddr(VkInstance instance, const char* pName) {
    if (icd.called_vk_icd_gipa == CalledICDGIPA::not_called) icd.called_vk_icd_gipa = CalledICDGIPA::vk_icd_gipa;
    return base_get_instance_proc_addr(instance, pName);
}
#else   // !TEST_ICD_EXPORT_ICD_GIPA
FRAMEWORK_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char* pName) {
    if (icd.called_vk_icd_gipa == CalledICDGIPA::not_called) icd.called_vk_icd_gipa = CalledICDGIPA::vk_gipa;
    return base_get_instance_proc_addr(instance, pName);
}
FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo,
                                                                 const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) {
    return test_vkCreateInstance(pCreateInfo, pAllocator, pInstance);
}
FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties(const char* pLayerName,
                                                                                       uint32_t* pPropertyCount,
                                                                                       VkExtensionProperties* pProperties) {
    return test_vkEnumerateInstanceExtensionProperties(pLayerName, pPropertyCount, pProperties);
}
#endif  // TEST_ICD_EXPORT_ICD_GIPA

#if TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES && TEST_ICD_EXPORT_VERSION_7
#if defined(WIN32)
FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vk_icdEnumerateAdapterPhysicalDevices(VkInstance instance, LUID adapterLUID,
                                                                                      uint32_t* pPhysicalDeviceCount,
                                                                                      VkPhysicalDevice* pPhysicalDevices) {
    return test_vk_icdEnumerateAdapterPhysicalDevices(instance, adapterLUID, pPhysicalDeviceCount, pPhysicalDevices);
}
#endif  // defined(WIN32)
#endif  // TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES

}  // extern "C"
