#define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h>

// Vulkan prototypes for use with a custom loader
#define VULKAN_FUNC_MAP(VULKAN_FUNC_MAP_MACRO) \
    VULKAN_FUNC_MAP_MACRO(vkAllocateCommandBuffers) \
    VULKAN_FUNC_MAP_MACRO(vkAllocateDescriptorSets) \
    VULKAN_FUNC_MAP_MACRO(vkAllocateMemory) \
    VULKAN_FUNC_MAP_MACRO(vkBindBufferMemory) \
    VULKAN_FUNC_MAP_MACRO(vkBindImageMemory) \
    VULKAN_FUNC_MAP_MACRO(vkCmdBindDescriptorSets) \
    VULKAN_FUNC_MAP_MACRO(vkCmdBindIndexBuffer) \
    VULKAN_FUNC_MAP_MACRO(vkCmdBindPipeline) \
    VULKAN_FUNC_MAP_MACRO(vkCmdBindVertexBuffers) \
    VULKAN_FUNC_MAP_MACRO(vkCmdCopyBufferToImage) \
    VULKAN_FUNC_MAP_MACRO(vkCmdDrawIndexed) \
    VULKAN_FUNC_MAP_MACRO(vkCmdPipelineBarrier) \
    VULKAN_FUNC_MAP_MACRO(vkCmdPushConstants) \
    VULKAN_FUNC_MAP_MACRO(vkCmdSetScissor) \
    VULKAN_FUNC_MAP_MACRO(vkCmdSetViewport) \
    VULKAN_FUNC_MAP_MACRO(vkCreateBuffer) \
    VULKAN_FUNC_MAP_MACRO(vkCreateCommandPool) \
    VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorSetLayout) \
    VULKAN_FUNC_MAP_MACRO(vkCreateFence) \
    VULKAN_FUNC_MAP_MACRO(vkCreateFramebuffer) \
    VULKAN_FUNC_MAP_MACRO(vkCreateGraphicsPipelines) \
    VULKAN_FUNC_MAP_MACRO(vkCreateImage) \
    VULKAN_FUNC_MAP_MACRO(vkCreateImageView) \
    VULKAN_FUNC_MAP_MACRO(vkCreatePipelineLayout) \
    VULKAN_FUNC_MAP_MACRO(vkCreateRenderPass) \
    VULKAN_FUNC_MAP_MACRO(vkCreateSampler) \
    VULKAN_FUNC_MAP_MACRO(vkCreateSemaphore) \
    VULKAN_FUNC_MAP_MACRO(vkCreateShaderModule) \
    VULKAN_FUNC_MAP_MACRO(vkCreateSwapchainKHR) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyBuffer) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyCommandPool) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorSetLayout) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyFence) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyFramebuffer) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyImage) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyImageView) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyPipeline) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyPipelineLayout) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyRenderPass) \
    VULKAN_FUNC_MAP_MACRO(vkDestroySampler) \
    VULKAN_FUNC_MAP_MACRO(vkDestroySemaphore) \
    VULKAN_FUNC_MAP_MACRO(vkDestroyShaderModule) \
    VULKAN_FUNC_MAP_MACRO(vkDestroySurfaceKHR) \
    VULKAN_FUNC_MAP_MACRO(vkDestroySwapchainKHR) \
    VULKAN_FUNC_MAP_MACRO(vkDeviceWaitIdle) \
    VULKAN_FUNC_MAP_MACRO(vkFlushMappedMemoryRanges) \
    VULKAN_FUNC_MAP_MACRO(vkFreeCommandBuffers) \
    VULKAN_FUNC_MAP_MACRO(vkFreeDescriptorSets) \
    VULKAN_FUNC_MAP_MACRO(vkFreeMemory) \
    VULKAN_FUNC_MAP_MACRO(vkGetBufferMemoryRequirements) \
    VULKAN_FUNC_MAP_MACRO(vkGetImageMemoryRequirements) \
    VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceMemoryProperties) \
    VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \
    VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \
    VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \
    VULKAN_FUNC_MAP_MACRO(vkGetSwapchainImagesKHR) \
    VULKAN_FUNC_MAP_MACRO(vkMapMemory) \
    VULKAN_FUNC_MAP_MACRO(vkUnmapMemory) \
    VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) \
    VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceSupportKHR) \
    VULKAN_FUNC_MAP_MACRO(vkWaitForFences) \
    VULKAN_FUNC_MAP_MACRO(vkCmdBeginRenderPass) \
    VULKAN_FUNC_MAP_MACRO(vkCmdEndRenderPass) \
    VULKAN_FUNC_MAP_MACRO(vkQueuePresentKHR) \
    VULKAN_FUNC_MAP_MACRO(vkBeginCommandBuffer) \
    VULKAN_FUNC_MAP_MACRO(vkEndCommandBuffer) \
    VULKAN_FUNC_MAP_MACRO(vkResetFences) \
    VULKAN_FUNC_MAP_MACRO(vkQueueSubmit) \
    VULKAN_FUNC_MAP_MACRO(vkResetCommandPool) \
    VULKAN_FUNC_MAP_MACRO(vkAcquireNextImageKHR)

#ifdef VK_NO_PROTOTYPES
// Define function pointers
#define VULKAN_FUNC_DEF(func) static PFN_##func func;
VULKAN_FUNC_MAP(VULKAN_FUNC_DEF)
#undef VULKAN_FUNC_DEF
#endif // VK_NO_PROTOTYPES

namespace Vulkan {

bool LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data)
{
    // Load function pointers
    // You can use the default Vulkan loader using:
    //      Vulkan::LoadFunctions([](const char* function_name, void*) { return vkGetInstanceProcAddr(vk_instance, function_name); });
    // But this would be equivalent to not setting VK_NO_PROTOTYPES.
#ifdef VK_NO_PROTOTYPES
#define VULKAN_FUNC_LOAD(func) \
    func = reinterpret_cast<decltype(func)>(loader_func(#func, user_data)); \
    if (func == nullptr)   \
        return false;
    VULKAN_FUNC_MAP(VULKAN_FUNC_LOAD)
#undef VULKAN_FUNC_LOAD
#endif // VK_NO_PROTOTYPES
    return true;
}

} // Vulkan namespace
