/*
 * Copyright 2017-2018 Roderick Colenbrander
 * Copyright 2022 Jacek Caban for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#ifndef __WINE_VULKAN_DRIVER_H
#define __WINE_VULKAN_DRIVER_H

#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>

#include <windef.h>
#include <winbase.h>

#include "wine/vulkan.h"

/* Base 'class' for our Vulkan dispatchable objects such as VkDevice and VkInstance.
 * This structure MUST be the first element of a dispatchable object as the ICD
 * loader depends on it. For now only contains loader_magic, but over time more common
 * functionality is expected.
 */
struct vulkan_client_object
{
    /* Special section in each dispatchable object for use by the ICD loader for
     * storing dispatch tables. The start contains a magical value '0x01CDC0DE'.
     */
    UINT64 loader_magic;
    UINT64 unix_handle;
};

struct vulkan_instance_extensions
{
#define USE_VK_EXT(x) unsigned has_ ## x : 1;
    ALL_VK_INSTANCE_EXTS
#undef USE_VK_EXT
};

struct vulkan_device_extensions
{
#define USE_VK_EXT(x) unsigned has_ ## x : 1;
    ALL_VK_DEVICE_EXTS
#undef USE_VK_EXT
};

struct VkPhysicalDevice_T
{
    struct vulkan_client_object obj;
    struct vulkan_device_extensions extensions;
};

struct VkInstance_T
{
    struct vulkan_client_object obj;
    struct vulkan_instance_extensions extensions;
    UINT physical_device_count;
    struct VkPhysicalDevice_T physical_device[1];
};

struct VkQueue_T
{
    struct vulkan_client_object obj;
};

struct VkDevice_T
{
    struct vulkan_client_object obj;
    struct vulkan_device_extensions extensions;
    struct VkQueue_T queues[1];
};

#ifdef WINE_UNIX_LIB

#include "wine/rbtree.h"
#include "wine/list.h"

/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */
#define WINE_VULKAN_DRIVER_VERSION 48

struct vulkan_object
{
    UINT64 host_handle;
    UINT64 client_handle;
    struct rb_entry entry;
};

#define VULKAN_OBJECT_HEADER( type, name ) \
    union { \
        struct vulkan_object obj; \
        struct { \
            union { type name; UINT64 handle; } host; \
            union { type name; UINT64 handle; } client; \
        }; \
    }

static inline void vulkan_object_init( struct vulkan_object *obj, UINT64 host_handle )
{
    obj->host_handle = host_handle;
    obj->client_handle = (UINT_PTR)obj;
}

static inline void vulkan_object_init_ptr( struct vulkan_object *obj, UINT64 host_handle, struct vulkan_client_object *client )
{
    obj->host_handle = host_handle;
    obj->client_handle = (UINT_PTR)client;
    client->unix_handle = (UINT_PTR)obj;
}

struct vulkan_debug_utils_messenger
{
    VULKAN_OBJECT_HEADER( VkDebugUtilsMessengerEXT, debug_messenger );
    struct vulkan_instance *instance;
    struct list entry; /* entry in instance list if static */

    UINT64 user_callback; /* client pointer */
    UINT64 user_data; /* client pointer */
};

static inline struct vulkan_debug_utils_messenger *vulkan_debug_utils_messenger_from_handle( VkDebugUtilsMessengerEXT handle )
{
    return (struct vulkan_debug_utils_messenger *)(UINT_PTR)handle;
}

struct vulkan_debug_report_callback
{
    VULKAN_OBJECT_HEADER( VkDebugReportCallbackEXT, debug_callback );
    struct vulkan_instance *instance;
    struct list entry; /* entry in instance list if static */

    UINT64 user_callback; /* client pointer */
    UINT64 user_data; /* client pointer */
};

static inline struct vulkan_debug_report_callback *vulkan_debug_report_callback_from_handle( VkDebugReportCallbackEXT handle )
{
    return (struct vulkan_debug_report_callback *)(UINT_PTR)handle;
}

struct vulkan_instance
{
    VULKAN_OBJECT_HEADER( VkInstance, instance );
    struct vulkan_instance_extensions extensions;
#define USE_VK_FUNC(x) PFN_ ## x p_ ## x;
    ALL_VK_INSTANCE_FUNCS
#undef USE_VK_FUNC
    void (*p_insert_object)( struct vulkan_instance *instance, struct vulkan_object *obj );
    void (*p_remove_object)( struct vulkan_instance *instance, struct vulkan_object *obj );
    uint64_t (*p_client_handle_from_host)( struct vulkan_instance *instance, uint64_t host_handle );

    struct vulkan_physical_device *physical_devices;
    uint32_t physical_device_count;
};

static inline struct vulkan_instance *vulkan_instance_from_handle( VkInstance handle )
{
    struct vulkan_client_object *client = (struct vulkan_client_object *)handle;
    return (struct vulkan_instance *)(UINT_PTR)client->unix_handle;
}

struct vulkan_physical_device
{
    VULKAN_OBJECT_HEADER( VkPhysicalDevice, physical_device );
    struct vulkan_instance *instance;
    struct vulkan_device_extensions extensions;

    /* for WOW64 memory mapping with VK_EXT_external_memory_host */
    VkPhysicalDeviceMemoryProperties memory_properties;
    uint32_t external_memory_align;
    uint32_t map_placed_align;
};

static inline struct vulkan_physical_device *vulkan_physical_device_from_handle( VkPhysicalDevice handle )
{
    struct vulkan_client_object *client = (struct vulkan_client_object *)handle;
    return (struct vulkan_physical_device *)(UINT_PTR)client->unix_handle;
}

struct vulkan_device;
struct vulkan_queue
{
    VULKAN_OBJECT_HEADER( VkQueue, queue );
    struct vulkan_device *device;
    VkDeviceQueueInfo2 info;
};

static inline struct vulkan_queue *vulkan_queue_from_handle( VkQueue handle )
{
    struct vulkan_client_object *client = (struct vulkan_client_object *)handle;
    return (struct vulkan_queue *)(UINT_PTR)client->unix_handle;
}

struct vulkan_device
{
    VULKAN_OBJECT_HEADER( VkDevice, device );
    struct vulkan_physical_device *physical_device;
    struct vulkan_device_extensions extensions;
#define USE_VK_FUNC(x) PFN_ ## x p_ ## x;
    ALL_VK_DEVICE_FUNCS
#undef USE_VK_FUNC

    uint64_t queue_count;
    struct vulkan_queue queues[];
};

C_ASSERT( sizeof(struct vulkan_device) == offsetof(struct vulkan_device, queues[0]) );

static inline struct vulkan_device *vulkan_device_from_handle( VkDevice handle )
{
    struct vulkan_client_object *client = (struct vulkan_client_object *)handle;
    return (struct vulkan_device *)(UINT_PTR)client->unix_handle;
}

struct vulkan_command_buffer
{
    VULKAN_OBJECT_HEADER( VkCommandBuffer, command_buffer );
    struct vulkan_device *device;
};

static inline struct vulkan_command_buffer *vulkan_command_buffer_from_handle( VkCommandBuffer handle )
{
    struct vulkan_client_object *client = (struct vulkan_client_object *)handle;
    return (struct vulkan_command_buffer *)(UINT_PTR)client->unix_handle;
}

struct vulkan_device_memory
{
    VULKAN_OBJECT_HEADER( VkDeviceMemory, device_memory );
};

static inline struct vulkan_device_memory *vulkan_device_memory_from_handle( VkDeviceMemory handle )
{
    return (struct vulkan_device_memory *)(UINT_PTR)handle;
}

struct vulkan_surface
{
    VULKAN_OBJECT_HEADER( VkSurfaceKHR, surface );
    struct vulkan_instance *instance;
};

static inline struct vulkan_surface *vulkan_surface_from_handle( VkSurfaceKHR handle )
{
    return (struct vulkan_surface *)(UINT_PTR)handle;
}

struct vulkan_swapchain
{
    VULKAN_OBJECT_HEADER( VkSwapchainKHR, swapchain );
};

static inline struct vulkan_swapchain *vulkan_swapchain_from_handle( VkSwapchainKHR handle )
{
    return (struct vulkan_swapchain *)(UINT_PTR)handle;
}

struct vulkan_semaphore
{
    VULKAN_OBJECT_HEADER( VkSemaphore, semaphore );
};

static inline struct vulkan_semaphore *vulkan_semaphore_from_handle( VkSemaphore handle )
{
    return (struct vulkan_semaphore *)(UINT_PTR)handle;
}

struct vulkan_fence
{
    VULKAN_OBJECT_HEADER( VkFence, fence );
};

static inline struct vulkan_fence *vulkan_fence_from_handle( VkFence handle )
{
    return (struct vulkan_fence *)(UINT_PTR)handle;
}

struct vulkan_funcs
{
    struct vulkan_instance_extensions host_extensions;
    struct vulkan_instance_extensions client_extensions;

    /* Vulkan global functions. These are the only calls at this point a graphics driver
     * needs to provide. Other function calls will be provided indirectly by dispatch
     * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.
     */
    PFN_vkAcquireNextImage2KHR p_vkAcquireNextImage2KHR;
    PFN_vkAcquireNextImageKHR p_vkAcquireNextImageKHR;
    PFN_vkAllocateMemory p_vkAllocateMemory;
    PFN_vkCreateBuffer p_vkCreateBuffer;
    PFN_vkCreateDevice p_vkCreateDevice;
    PFN_vkCreateFence p_vkCreateFence;
    PFN_vkCreateImage p_vkCreateImage;
    PFN_vkCreateInstance p_vkCreateInstance;
    PFN_vkCreateSemaphore p_vkCreateSemaphore;
    PFN_vkCreateSwapchainKHR p_vkCreateSwapchainKHR;
    PFN_vkCreateWin32SurfaceKHR p_vkCreateWin32SurfaceKHR;
    PFN_vkDestroyDevice p_vkDestroyDevice;
    PFN_vkDestroyFence p_vkDestroyFence;
    PFN_vkDestroyInstance p_vkDestroyInstance;
    PFN_vkDestroySemaphore p_vkDestroySemaphore;
    PFN_vkDestroySurfaceKHR p_vkDestroySurfaceKHR;
    PFN_vkDestroySwapchainKHR p_vkDestroySwapchainKHR;
    PFN_vkFreeMemory p_vkFreeMemory;
    PFN_vkGetDeviceBufferMemoryRequirements p_vkGetDeviceBufferMemoryRequirements;
    PFN_vkGetDeviceBufferMemoryRequirementsKHR p_vkGetDeviceBufferMemoryRequirementsKHR;
    PFN_vkGetDeviceImageMemoryRequirements p_vkGetDeviceImageMemoryRequirements;
    PFN_vkGetDeviceProcAddr p_vkGetDeviceProcAddr;
    PFN_vkGetDeviceQueue p_vkGetDeviceQueue;
    PFN_vkGetDeviceQueue2 p_vkGetDeviceQueue2;
    PFN_vkGetFenceWin32HandleKHR p_vkGetFenceWin32HandleKHR;
    PFN_vkGetInstanceProcAddr p_vkGetInstanceProcAddr;
    PFN_vkGetMemoryWin32HandleKHR p_vkGetMemoryWin32HandleKHR;
    PFN_vkGetMemoryWin32HandlePropertiesKHR p_vkGetMemoryWin32HandlePropertiesKHR;
    PFN_vkGetPhysicalDeviceExternalBufferProperties p_vkGetPhysicalDeviceExternalBufferProperties;
    PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR p_vkGetPhysicalDeviceExternalBufferPropertiesKHR;
    PFN_vkGetPhysicalDeviceExternalFenceProperties p_vkGetPhysicalDeviceExternalFenceProperties;
    PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR p_vkGetPhysicalDeviceExternalFencePropertiesKHR;
    PFN_vkGetPhysicalDeviceExternalSemaphoreProperties p_vkGetPhysicalDeviceExternalSemaphoreProperties;
    PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR p_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR;
    PFN_vkGetPhysicalDeviceImageFormatProperties2 p_vkGetPhysicalDeviceImageFormatProperties2;
    PFN_vkGetPhysicalDeviceImageFormatProperties2KHR p_vkGetPhysicalDeviceImageFormatProperties2KHR;
    PFN_vkGetPhysicalDevicePresentRectanglesKHR p_vkGetPhysicalDevicePresentRectanglesKHR;
    PFN_vkGetPhysicalDeviceProperties p_vkGetPhysicalDeviceProperties;
    PFN_vkGetPhysicalDeviceProperties2 p_vkGetPhysicalDeviceProperties2;
    PFN_vkGetPhysicalDeviceProperties2KHR p_vkGetPhysicalDeviceProperties2KHR;
    PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR p_vkGetPhysicalDeviceSurfaceCapabilities2KHR;
    PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR p_vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
    PFN_vkGetPhysicalDeviceSurfaceFormats2KHR p_vkGetPhysicalDeviceSurfaceFormats2KHR;
    PFN_vkGetPhysicalDeviceSurfaceFormatsKHR p_vkGetPhysicalDeviceSurfaceFormatsKHR;
    PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR p_vkGetPhysicalDeviceWin32PresentationSupportKHR;
    PFN_vkGetSemaphoreWin32HandleKHR p_vkGetSemaphoreWin32HandleKHR;
    PFN_vkImportFenceWin32HandleKHR p_vkImportFenceWin32HandleKHR;
    PFN_vkImportSemaphoreWin32HandleKHR p_vkImportSemaphoreWin32HandleKHR;
    PFN_vkMapMemory p_vkMapMemory;
    PFN_vkMapMemory2KHR p_vkMapMemory2KHR;
    PFN_vkQueuePresentKHR p_vkQueuePresentKHR;
    PFN_vkQueueSubmit p_vkQueueSubmit;
    PFN_vkQueueSubmit2 p_vkQueueSubmit2;
    PFN_vkQueueSubmit2KHR p_vkQueueSubmit2KHR;
    PFN_vkUnmapMemory p_vkUnmapMemory;
    PFN_vkUnmapMemory2KHR p_vkUnmapMemory2KHR;
};

/* interface between win32u and the user drivers */
struct client_surface;
struct vulkan_driver_funcs
{
    VkResult (*p_vulkan_surface_create)(struct client_surface *, const struct vulkan_instance *, VkSurfaceKHR *);
    VkBool32 (*p_get_physical_device_presentation_support)(struct vulkan_physical_device *, uint32_t);
    void (*p_map_instance_extensions)( struct vulkan_instance_extensions *extensions );
    void (*p_map_device_extensions)( struct vulkan_device_extensions *extensions );
};

#endif /* WINE_UNIX_LIB */

#endif /* __WINE_VULKAN_DRIVER_H */
