1 /* Mac Driver Vulkan implementation
3 * Copyright 2017 Roderick Colenbrander
4 * Copyright 2018 Andrew Eikum for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 /* NOTE: If making changes here, consider whether they should be reflected in
22 * the other drivers. */
25 #include "wine/port.h"
34 #include "wine/debug.h"
35 #include "wine/heap.h"
36 #include "wine/library.h"
38 #define VK_NO_PROTOTYPES
41 #include "wine/vulkan.h"
42 #include "wine/vulkan_driver.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(vulkan
);
46 #ifdef SONAME_LIBMOLTENVK
48 typedef VkFlags VkMacOSSurfaceCreateFlagsMVK
;
49 #define VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK 1000123000
51 struct wine_vk_surface
53 macdrv_metal_device device
;
54 macdrv_metal_view view
;
55 VkSurfaceKHR surface
; /* native surface */
58 typedef struct VkMacOSSurfaceCreateInfoMVK
60 VkStructureType sType
;
62 VkMacOSSurfaceCreateFlagsMVK flags
;
63 const void *pView
; /* NSView */
64 } VkMacOSSurfaceCreateInfoMVK
;
66 static VkResult (*pvkCreateInstance
)(const VkInstanceCreateInfo
*, const VkAllocationCallbacks
*, VkInstance
*);
67 static VkResult (*pvkCreateSwapchainKHR
)(VkDevice
, const VkSwapchainCreateInfoKHR
*, const VkAllocationCallbacks
*, VkSwapchainKHR
*);
68 static VkResult (*pvkCreateMacOSSurfaceMVK
)(VkInstance
, const VkMacOSSurfaceCreateInfoMVK
*, const VkAllocationCallbacks
*, VkSurfaceKHR
*);
69 static void (*pvkDestroyInstance
)(VkInstance
, const VkAllocationCallbacks
*);
70 static void (*pvkDestroySurfaceKHR
)(VkInstance
, VkSurfaceKHR
, const VkAllocationCallbacks
*);
71 static void (*pvkDestroySwapchainKHR
)(VkDevice
, VkSwapchainKHR
, const VkAllocationCallbacks
*);
72 static VkResult (*pvkEnumerateInstanceExtensionProperties
)(const char *, uint32_t *, VkExtensionProperties
*);
73 static void * (*pvkGetDeviceProcAddr
)(VkDevice
, const char *);
74 static void * (*pvkGetInstanceProcAddr
)(VkInstance
, const char *);
75 static VkResult (*pvkGetPhysicalDeviceSurfaceCapabilitiesKHR
)(VkPhysicalDevice
, VkSurfaceKHR
, VkSurfaceCapabilitiesKHR
*);
76 static VkResult (*pvkGetPhysicalDeviceSurfaceFormatsKHR
)(VkPhysicalDevice
, VkSurfaceKHR
, uint32_t *, VkSurfaceFormatKHR
*);
77 static VkResult (*pvkGetPhysicalDeviceSurfacePresentModesKHR
)(VkPhysicalDevice
, VkSurfaceKHR
, uint32_t *, VkPresentModeKHR
*);
78 static VkResult (*pvkGetPhysicalDeviceSurfaceSupportKHR
)(VkPhysicalDevice
, uint32_t, VkSurfaceKHR
, VkBool32
*);
79 static VkResult (*pvkGetSwapchainImagesKHR
)(VkDevice
, VkSwapchainKHR
, uint32_t *, VkImage
*);
80 static VkResult (*pvkQueuePresentKHR
)(VkQueue
, const VkPresentInfoKHR
*);
82 static void *macdrv_get_vk_device_proc_addr(const char *name
);
83 static void *macdrv_get_vk_instance_proc_addr(VkInstance instance
, const char *name
);
85 static inline struct wine_vk_surface
*surface_from_handle(VkSurfaceKHR handle
)
87 return (struct wine_vk_surface
*)(uintptr_t)handle
;
90 static void *vulkan_handle
;
92 static BOOL WINAPI
wine_vk_init(INIT_ONCE
*once
, void *param
, void **context
)
94 if (!(vulkan_handle
= wine_dlopen(SONAME_LIBMOLTENVK
, RTLD_NOW
, NULL
, 0)))
96 ERR("Failed to load %s\n", SONAME_LIBMOLTENVK
);
100 #define LOAD_FUNCPTR(f) if ((p##f = wine_dlsym(vulkan_handle, #f, NULL, 0)) == NULL) goto fail;
101 LOAD_FUNCPTR(vkCreateInstance
)
102 LOAD_FUNCPTR(vkCreateSwapchainKHR
)
103 LOAD_FUNCPTR(vkCreateMacOSSurfaceMVK
)
104 LOAD_FUNCPTR(vkDestroyInstance
)
105 LOAD_FUNCPTR(vkDestroySurfaceKHR
)
106 LOAD_FUNCPTR(vkDestroySwapchainKHR
)
107 LOAD_FUNCPTR(vkEnumerateInstanceExtensionProperties
)
108 LOAD_FUNCPTR(vkGetDeviceProcAddr
)
109 LOAD_FUNCPTR(vkGetInstanceProcAddr
)
110 LOAD_FUNCPTR(vkGetPhysicalDeviceSurfaceCapabilitiesKHR
)
111 LOAD_FUNCPTR(vkGetPhysicalDeviceSurfaceFormatsKHR
)
112 LOAD_FUNCPTR(vkGetPhysicalDeviceSurfacePresentModesKHR
)
113 LOAD_FUNCPTR(vkGetPhysicalDeviceSurfaceSupportKHR
)
114 LOAD_FUNCPTR(vkGetSwapchainImagesKHR
)
115 LOAD_FUNCPTR(vkQueuePresentKHR
)
121 wine_dlclose(vulkan_handle
, NULL
, 0);
122 vulkan_handle
= NULL
;
126 /* Helper function for converting between win32 and MoltenVK compatible VkInstanceCreateInfo.
127 * Caller is responsible for allocation and cleanup of 'dst'.
129 static VkResult
wine_vk_instance_convert_create_info(const VkInstanceCreateInfo
*src
,
130 VkInstanceCreateInfo
*dst
)
133 const char **enabled_extensions
= NULL
;
135 dst
->sType
= src
->sType
;
136 dst
->flags
= src
->flags
;
137 dst
->pApplicationInfo
= src
->pApplicationInfo
;
138 dst
->pNext
= src
->pNext
;
139 dst
->enabledLayerCount
= 0;
140 dst
->ppEnabledLayerNames
= NULL
;
141 dst
->enabledExtensionCount
= 0;
142 dst
->ppEnabledExtensionNames
= NULL
;
144 if (src
->enabledExtensionCount
> 0)
146 enabled_extensions
= heap_calloc(src
->enabledExtensionCount
, sizeof(*src
->ppEnabledExtensionNames
));
147 if (!enabled_extensions
)
149 ERR("Failed to allocate memory for enabled extensions\n");
150 return VK_ERROR_OUT_OF_HOST_MEMORY
;
153 for (i
= 0; i
< src
->enabledExtensionCount
; i
++)
155 /* Substitute extension with MoltenVK ones else copy. Long-term, when we
156 * support more extensions, we should store these in a list.
158 if (!strcmp(src
->ppEnabledExtensionNames
[i
], "VK_KHR_win32_surface"))
160 enabled_extensions
[i
] = "VK_MVK_macos_surface";
164 enabled_extensions
[i
] = src
->ppEnabledExtensionNames
[i
];
167 dst
->ppEnabledExtensionNames
= enabled_extensions
;
168 dst
->enabledExtensionCount
= src
->enabledExtensionCount
;
174 static void wine_vk_surface_destroy(VkInstance instance
, struct wine_vk_surface
*surface
)
176 /* vkDestroySurfaceKHR must handle VK_NULL_HANDLE (0) for surface. */
180 pvkDestroySurfaceKHR(instance
, surface
->surface
, NULL
/* allocator */);
183 macdrv_view_release_metal_view(surface
->view
);
186 macdrv_release_metal_device(surface
->device
);
191 static VkResult
macdrv_vkCreateInstance(const VkInstanceCreateInfo
*create_info
,
192 const VkAllocationCallbacks
*allocator
, VkInstance
*instance
)
194 VkInstanceCreateInfo create_info_host
;
196 TRACE("create_info %p, allocator %p, instance %p\n", create_info
, allocator
, instance
);
199 FIXME("Support for allocation callbacks not implemented yet\n");
201 /* Perform a second pass on converting VkInstanceCreateInfo. Winevulkan
202 * performed a first pass in which it handles everything except for WSI
203 * functionality such as VK_KHR_win32_surface. Handle this now.
205 res
= wine_vk_instance_convert_create_info(create_info
, &create_info_host
);
206 if (res
!= VK_SUCCESS
)
208 ERR("Failed to convert instance create info, res=%d\n", res
);
212 res
= pvkCreateInstance(&create_info_host
, NULL
/* allocator */, instance
);
214 heap_free((void *)create_info_host
.ppEnabledExtensionNames
);
218 static VkResult
macdrv_vkCreateSwapchainKHR(VkDevice device
,
219 const VkSwapchainCreateInfoKHR
*create_info
,
220 const VkAllocationCallbacks
*allocator
, VkSwapchainKHR
*swapchain
)
222 VkSwapchainCreateInfoKHR create_info_host
;
223 TRACE("%p %p %p %p\n", device
, create_info
, allocator
, swapchain
);
226 FIXME("Support for allocation callbacks not implemented yet\n");
228 create_info_host
= *create_info
;
229 create_info_host
.surface
= surface_from_handle(create_info
->surface
)->surface
;
231 return pvkCreateSwapchainKHR(device
, &create_info_host
, NULL
/* allocator */,
235 static VkResult
macdrv_vkCreateWin32SurfaceKHR(VkInstance instance
,
236 const VkWin32SurfaceCreateInfoKHR
*create_info
,
237 const VkAllocationCallbacks
*allocator
, VkSurfaceKHR
*surface
)
240 VkMacOSSurfaceCreateInfoMVK create_info_host
;
241 struct wine_vk_surface
*mac_surface
;
242 struct macdrv_win_data
*data
;
244 TRACE("%p %p %p %p\n", instance
, create_info
, allocator
, surface
);
247 FIXME("Support for allocation callbacks not implemented yet\n");
249 if (!(data
= get_win_data(create_info
->hwnd
)))
251 FIXME("DC for window %p of other process: not implemented\n", create_info
->hwnd
);
252 return VK_ERROR_INCOMPATIBLE_DRIVER
;
255 mac_surface
= heap_alloc_zero(sizeof(*mac_surface
));
258 release_win_data(data
);
259 return VK_ERROR_OUT_OF_HOST_MEMORY
;
262 mac_surface
->device
= macdrv_create_metal_device();
263 if (!mac_surface
->device
)
265 ERR("Failed to allocate Metal device for hwnd=%p\n", create_info
->hwnd
);
266 res
= VK_ERROR_OUT_OF_HOST_MEMORY
;
270 mac_surface
->view
= macdrv_view_create_metal_view(data
->client_cocoa_view
, mac_surface
->device
);
271 if (!mac_surface
->view
)
273 ERR("Failed to allocate Metal view for hwnd=%p\n", create_info
->hwnd
);
275 /* VK_KHR_win32_surface only allows out of host and device memory as errors. */
276 res
= VK_ERROR_OUT_OF_HOST_MEMORY
;
280 create_info_host
.sType
= VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK
;
281 create_info_host
.pNext
= NULL
;
282 create_info_host
.flags
= 0; /* reserved */
283 create_info_host
.pView
= mac_surface
->view
;
285 res
= pvkCreateMacOSSurfaceMVK(instance
, &create_info_host
, NULL
/* allocator */, &mac_surface
->surface
);
286 if (res
!= VK_SUCCESS
)
288 ERR("Failed to create MoltenVK surface, res=%d\n", res
);
292 *surface
= (uintptr_t)mac_surface
;
294 release_win_data(data
);
296 TRACE("Created surface=0x%s\n", wine_dbgstr_longlong(*surface
));
300 wine_vk_surface_destroy(instance
, mac_surface
);
301 release_win_data(data
);
305 static void macdrv_vkDestroyInstance(VkInstance instance
, const VkAllocationCallbacks
*allocator
)
307 TRACE("%p %p\n", instance
, allocator
);
310 FIXME("Support for allocation callbacks not implemented yet\n");
312 pvkDestroyInstance(instance
, NULL
/* allocator */);
315 static void macdrv_vkDestroySurfaceKHR(VkInstance instance
, VkSurfaceKHR surface
,
316 const VkAllocationCallbacks
*allocator
)
318 struct wine_vk_surface
*mac_surface
= surface_from_handle(surface
);
320 TRACE("%p 0x%s %p\n", instance
, wine_dbgstr_longlong(surface
), allocator
);
323 FIXME("Support for allocation callbacks not implemented yet\n");
325 wine_vk_surface_destroy(instance
, mac_surface
);
328 static void macdrv_vkDestroySwapchainKHR(VkDevice device
, VkSwapchainKHR swapchain
,
329 const VkAllocationCallbacks
*allocator
)
331 TRACE("%p, 0x%s %p\n", device
, wine_dbgstr_longlong(swapchain
), allocator
);
334 FIXME("Support for allocation callbacks not implemented yet\n");
336 pvkDestroySwapchainKHR(device
, swapchain
, NULL
/* allocator */);
339 static VkResult
macdrv_vkEnumerateInstanceExtensionProperties(const char *layer_name
,
340 uint32_t *count
, VkExtensionProperties
* properties
)
345 TRACE("layer_name %s, count %p, properties %p\n", debugstr_a(layer_name
), count
, properties
);
347 /* This shouldn't get called with layer_name set, the ICD loader prevents it. */
350 ERR("Layer enumeration not supported from ICD.\n");
351 return VK_ERROR_LAYER_NOT_PRESENT
;
354 /* We will return the same number of instance extensions reported by the host back to
355 * winevulkan. Along the way we may replace MoltenVK extensions with their win32 equivalents.
356 * Winevulkan will perform more detailed filtering as it knows whether it has thunks
357 * for a particular extension.
359 res
= pvkEnumerateInstanceExtensionProperties(layer_name
, count
, properties
);
360 if (!properties
|| res
< 0)
363 for (i
= 0; i
< *count
; i
++)
365 /* For now the only MoltenVK extension we need to fixup. Long-term we may need an array. */
366 if (!strcmp(properties
[i
].extensionName
, "VK_MVK_macos_surface"))
368 TRACE("Substituting VK_MVK_macos_surface for VK_KHR_win32_surface\n");
370 snprintf(properties
[i
].extensionName
, sizeof(properties
[i
].extensionName
),
371 VK_KHR_WIN32_SURFACE_EXTENSION_NAME
);
372 properties
[i
].specVersion
= VK_KHR_WIN32_SURFACE_SPEC_VERSION
;
376 TRACE("Returning %u extensions.\n", *count
);
380 static void *macdrv_vkGetDeviceProcAddr(VkDevice device
, const char *name
)
384 TRACE("%p, %s\n", device
, debugstr_a(name
));
386 if ((proc_addr
= macdrv_get_vk_device_proc_addr(name
)))
389 return pvkGetDeviceProcAddr(device
, name
);
392 static void *macdrv_vkGetInstanceProcAddr(VkInstance instance
, const char *name
)
396 TRACE("%p, %s\n", instance
, debugstr_a(name
));
398 if ((proc_addr
= macdrv_get_vk_instance_proc_addr(instance
, name
)))
401 return pvkGetInstanceProcAddr(instance
, name
);
404 static VkResult
macdrv_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice phys_dev
,
405 VkSurfaceKHR surface
, VkSurfaceCapabilitiesKHR
*capabilities
)
407 struct wine_vk_surface
*mac_surface
= surface_from_handle(surface
);
409 TRACE("%p, 0x%s, %p\n", phys_dev
, wine_dbgstr_longlong(surface
), capabilities
);
411 return pvkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys_dev
, mac_surface
->surface
,
415 static VkResult
macdrv_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice phys_dev
,
416 VkSurfaceKHR surface
, uint32_t *count
, VkSurfaceFormatKHR
*formats
)
418 struct wine_vk_surface
*mac_surface
= surface_from_handle(surface
);
420 TRACE("%p, 0x%s, %p, %p\n", phys_dev
, wine_dbgstr_longlong(surface
), count
, formats
);
422 return pvkGetPhysicalDeviceSurfaceFormatsKHR(phys_dev
, mac_surface
->surface
,
426 static VkResult
macdrv_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice phys_dev
,
427 VkSurfaceKHR surface
, uint32_t *count
, VkPresentModeKHR
*modes
)
429 struct wine_vk_surface
*mac_surface
= surface_from_handle(surface
);
431 TRACE("%p, 0x%s, %p, %p\n", phys_dev
, wine_dbgstr_longlong(surface
), count
, modes
);
433 return pvkGetPhysicalDeviceSurfacePresentModesKHR(phys_dev
, mac_surface
->surface
, count
,
437 static VkResult
macdrv_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice phys_dev
,
438 uint32_t index
, VkSurfaceKHR surface
, VkBool32
*supported
)
440 struct wine_vk_surface
*mac_surface
= surface_from_handle(surface
);
442 TRACE("%p, %u, 0x%s, %p\n", phys_dev
, index
, wine_dbgstr_longlong(surface
), supported
);
444 return pvkGetPhysicalDeviceSurfaceSupportKHR(phys_dev
, index
, mac_surface
->surface
,
448 static VkBool32
macdrv_vkGetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice phys_dev
,
451 TRACE("%p %u\n", phys_dev
, index
);
456 static VkResult
macdrv_vkGetSwapchainImagesKHR(VkDevice device
,
457 VkSwapchainKHR swapchain
, uint32_t *count
, VkImage
*images
)
459 TRACE("%p, 0x%s %p %p\n", device
, wine_dbgstr_longlong(swapchain
), count
, images
);
460 return pvkGetSwapchainImagesKHR(device
, swapchain
, count
, images
);
463 static VkResult
macdrv_vkQueuePresentKHR(VkQueue queue
, const VkPresentInfoKHR
*present_info
)
465 TRACE("%p, %p\n", queue
, present_info
);
466 return pvkQueuePresentKHR(queue
, present_info
);
469 static const struct vulkan_funcs vulkan_funcs
=
471 macdrv_vkCreateInstance
,
472 macdrv_vkCreateSwapchainKHR
,
473 macdrv_vkCreateWin32SurfaceKHR
,
474 macdrv_vkDestroyInstance
,
475 macdrv_vkDestroySurfaceKHR
,
476 macdrv_vkDestroySwapchainKHR
,
477 macdrv_vkEnumerateInstanceExtensionProperties
,
479 macdrv_vkGetDeviceProcAddr
,
480 macdrv_vkGetInstanceProcAddr
,
482 macdrv_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
,
483 macdrv_vkGetPhysicalDeviceSurfaceFormatsKHR
,
484 macdrv_vkGetPhysicalDeviceSurfacePresentModesKHR
,
485 macdrv_vkGetPhysicalDeviceSurfaceSupportKHR
,
486 macdrv_vkGetPhysicalDeviceWin32PresentationSupportKHR
,
487 macdrv_vkGetSwapchainImagesKHR
,
488 macdrv_vkQueuePresentKHR
,
491 static void *macdrv_get_vk_device_proc_addr(const char *name
)
493 return get_vulkan_driver_device_proc_addr(&vulkan_funcs
, name
);
496 static void *macdrv_get_vk_instance_proc_addr(VkInstance instance
, const char *name
)
498 return get_vulkan_driver_instance_proc_addr(&vulkan_funcs
, instance
, name
);
501 static const struct vulkan_funcs
*get_vulkan_driver(UINT version
)
503 static INIT_ONCE init_once
= INIT_ONCE_STATIC_INIT
;
505 if (version
!= WINE_VULKAN_DRIVER_VERSION
)
507 ERR("version mismatch, vulkan wants %u but driver has %u\n", version
, WINE_VULKAN_DRIVER_VERSION
);
511 InitOnceExecuteOnce(&init_once
, wine_vk_init
, NULL
, NULL
);
513 return &vulkan_funcs
;
518 #else /* No vulkan */
520 static const struct vulkan_funcs
*get_vulkan_driver(UINT version
)
522 ERR("Wine was built without Vulkan support.\n");
526 #endif /* SONAME_LIBMOLTENVK */
528 const struct vulkan_funcs
*macdrv_wine_get_vulkan_driver(PHYSDEV dev
, UINT version
)
530 const struct vulkan_funcs
*ret
;
532 if (!(ret
= get_vulkan_driver( version
)))
534 dev
= GET_NEXT_PHYSDEV( dev
, wine_get_vulkan_driver
);
535 ret
= dev
->funcs
->wine_get_vulkan_driver( dev
, version
);