On Sat Nov 23 14:38:08 2024 +0000, Rémi Bernon wrote:
What would you think of an interface in `wine/vulkan_driver.h` like this one:
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; }; #ifdef WINE_UNIX_LIB struct vulkan_object { UINT64 host_handle; UINT64 client_handle; struct vulkan_object *parent; struct rb_entry entry; }; #define VULKAN_OBJECT_HEADER( type, name, parent_type, parent_name ) \ union { \ struct vulkan_object obj; \ struct { \ union { type name; UINT64 handle; } host; \ union { type name; UINT64 handle; } client; \ parent_type *parent_name; \ }; \ } static inline void vulkan_object_init( struct vulkan_object *obj, struct vulkan_object *parent, UINT64 host_handle, struct vulkan_client_object *client ) { obj->host_handle = (UINT_PTR)host_handle; obj->client_handle = client ? (UINT_PTR)client : (UINT_PTR)obj; obj->parent = parent; if (client) client->unix_handle = (UINT_PTR)obj; } static inline void vulkan_object_init_ptr( struct vulkan_object *obj, struct vulkan_object *parent, void *host_handle, struct vulkan_client_object *client ) { vulkan_object_init( obj, parent, (UINT_PTR)host_handle, client ); } struct vulkan_instance { VULKAN_OBJECT_HEADER( VkInstance, instance, void, unused ); struct vulkan_instance_funcs funcs; void (*p_insert_object)( struct vulkan_instance *instance, struct vulkan_object *obj ); void (*p_remove_object)( struct vulkan_instance *instance, struct vulkan_object *obj ); }; 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 ); }; 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 { VULKAN_OBJECT_HEADER( VkDevice, device, struct vulkan_physical_device, physical_device ); struct vulkan_device_funcs funcs; }; 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_queue { VULKAN_OBJECT_HEADER( VkQueue, queue, struct vulkan_device, device ); }; 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_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, struct vulkan_surface, surface ); }; static inline struct vulkan_swapchain *vulkan_swapchain_from_handle( VkSwapchainKHR handle ) { return (struct vulkan_swapchain *)(UINT_PTR)handle; } struct vulkan_funcs { /* 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_vkGetDeviceProcAddr p_vkGetDeviceProcAddr; PFN_vkGetInstanceProcAddr p_vkGetInstanceProcAddr; /* winevulkan specific functions */ const char *(*p_get_host_surface_extension)(void); }; /* interface between win32u and the user drivers */ struct vulkan_driver_funcs { VkResult (*p_vulkan_surface_create)(HWND, VkInstance, VkSurfaceKHR *, void **); void (*p_vulkan_surface_destroy)(HWND, void *); void (*p_vulkan_surface_detach)(HWND, void *); void (*p_vulkan_surface_update)(HWND, void *); void (*p_vulkan_surface_presented)(HWND, void *, VkResult); VkBool32 (*p_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice, uint32_t); const char *(*p_get_host_surface_extension)(void); }; #endif /* WINE_UNIX_LIB */
It's sharing the minimum required, each object has its own type but the macro is there to ensure a common header layout (mostly to simplify insert_object/remove_object, which can then take a simple object pointer), and the instance exports two function pointers to register object wrappers, regardless of where it is wrapped. With that I can move the surface/swapchain wrappers to win32u, while keeping only one level of indirection, while the other objects are still wrapped in winevulkan. It doesn't seem useful to move other wrappers to win32u for now, especially as some of them are more or less strongly coupled together: wine_instance uses the debug objects, wine_device_memory wrapper needs some info in the wine_phys_dev, etc. Also note that I'm thinking we could have an OpenXR icd upstream, similarly to winevulkan, for host OpenXR integration (and it'd make our life easier with Proton), and OpenXR is also interacting quite deeply with Vulkan, and would probably need to have access to the instance and device wrappers. This would mean either moving them somehow (and maybe all the wrappers) to some VK/XR shared win32u code, or use winevulkan as the XR icd too.
This looks improved, but I’m not sure why the `parent` needs to be part of `vulkan_object`, or why its declaration is hidden behind a macro. Couldn’t it simply be a pointer in the type-specific structs instead?
Additionally, including `vulkan_device_funcs` and `vulkan_instance_funcs` in `wine/vulkan.h` seems like it blurs the (arguably vague) separation between `vulkan.h` and `vulkan_driver.h`. Would it make sense to define `ALL_VK_DEVICE_FUNCS` and `ALL_VK_INSTANCE_FUNCS` in `vulkan.h`, and then use those to declare the structs in `vulkan_driver.h`?
(Do we even need these `*_funcs` structs? We instead store function pointers directly in `vulkan_instance`/`vulkan_device`.)