Allows running more tests with nulldrv, including some D3D tests with vulkan renderer.
-- v2: win32u: Use VK_EXT_headless_surface for nulldrv surface. winevulkan: Enable VK_EXT_headless_surface extension.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winevulkan/loader_thunks.c | 14 +++++++++ dlls/winevulkan/loader_thunks.h | 10 +++++++ dlls/winevulkan/make_vulkan | 3 +- dlls/winevulkan/vulkan_thunks.c | 52 +++++++++++++++++++++++++++++++++ include/wine/vulkan.h | 13 +++++++++ 5 files changed, 90 insertions(+), 2 deletions(-)
diff --git a/dlls/winevulkan/loader_thunks.c b/dlls/winevulkan/loader_thunks.c index 184d13fd977..0aab3aafdd9 100644 --- a/dlls/winevulkan/loader_thunks.c +++ b/dlls/winevulkan/loader_thunks.c @@ -3191,6 +3191,19 @@ VkResult WINAPI vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipel return params.result; }
+VkResult WINAPI vkCreateHeadlessSurfaceEXT(VkInstance instance, const VkHeadlessSurfaceCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) +{ + struct vkCreateHeadlessSurfaceEXT_params params; + NTSTATUS status; + params.instance = instance; + params.pCreateInfo = pCreateInfo; + params.pAllocator = pAllocator; + params.pSurface = pSurface; + status = UNIX_CALL(vkCreateHeadlessSurfaceEXT, ¶ms); + assert(!status && "vkCreateHeadlessSurfaceEXT"); + return params.result; +} + VkResult WINAPI vkCreateImage(VkDevice device, const VkImageCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImage *pImage) { struct vkCreateImage_params params; @@ -7435,6 +7448,7 @@ static const struct vulkan_func vk_instance_dispatch_table[] = { {"vkCreateDebugReportCallbackEXT", vkCreateDebugReportCallbackEXT}, {"vkCreateDebugUtilsMessengerEXT", vkCreateDebugUtilsMessengerEXT}, + {"vkCreateHeadlessSurfaceEXT", vkCreateHeadlessSurfaceEXT}, {"vkCreateWin32SurfaceKHR", vkCreateWin32SurfaceKHR}, {"vkDebugReportMessageEXT", vkDebugReportMessageEXT}, {"vkDestroyDebugReportCallbackEXT", vkDestroyDebugReportCallbackEXT}, diff --git a/dlls/winevulkan/loader_thunks.h b/dlls/winevulkan/loader_thunks.h index ec7b9645e5b..b85fd2890c8 100644 --- a/dlls/winevulkan/loader_thunks.h +++ b/dlls/winevulkan/loader_thunks.h @@ -344,6 +344,7 @@ enum unix_call unix_vkCreateFence, unix_vkCreateFramebuffer, unix_vkCreateGraphicsPipelines, + unix_vkCreateHeadlessSurfaceEXT, unix_vkCreateImage, unix_vkCreateImageView, unix_vkCreateIndirectCommandsLayoutEXT, @@ -3113,6 +3114,15 @@ struct vkCreateGraphicsPipelines_params VkResult result; };
+struct vkCreateHeadlessSurfaceEXT_params +{ + VkInstance instance; + const VkHeadlessSurfaceCreateInfoEXT *pCreateInfo; + const VkAllocationCallbacks *pAllocator; + VkSurfaceKHR *pSurface; + VkResult result; +}; + struct vkCreateImage_params { VkDevice device; diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 64233268cf1..b3a2eb29987 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -88,7 +88,6 @@ EXT_BLOCK_SIZE = 1000
UNSUPPORTED_EXTENSIONS = [ # Instance extensions - "VK_EXT_headless_surface", # Needs WSI work. "VK_KHR_display", # Needs WSI work. "VK_KHR_surface_protected_capabilities", "VK_LUNARG_direct_driver_loading", # Implemented in the Vulkan loader @@ -1976,7 +1975,7 @@ class VkParam(VkVariable): unwrap_handle = "wine_vk_unwrap_handle({0}{1}, {0}{2})".format( params_prefix, self.object_type, self.name)
- elif self.is_handle(): + elif self.is_handle() and not self.is_pointer(): # We need to pass the host handle to the host Vulkan calls and # the wine driver's handle to calls which are wrapped by the driver. unwrap_handle = self.handle.unwrap_handle(p, unwrap) diff --git a/dlls/winevulkan/vulkan_thunks.c b/dlls/winevulkan/vulkan_thunks.c index d4e415fd056..ba83e28bfab 100644 --- a/dlls/winevulkan/vulkan_thunks.c +++ b/dlls/winevulkan/vulkan_thunks.c @@ -5063,6 +5063,13 @@ typedef struct VkGraphicsPipelineCreateInfo32 int32_t basePipelineIndex; } VkGraphicsPipelineCreateInfo32;
+typedef struct VkHeadlessSurfaceCreateInfoEXT32 +{ + VkStructureType sType; + PTR32 pNext; + VkHeadlessSurfaceCreateFlagsEXT flags; +} VkHeadlessSurfaceCreateInfoEXT32; + typedef struct VkDedicatedAllocationImageCreateInfoNV32 { VkStructureType sType; @@ -22319,6 +22326,17 @@ static inline void convert_VkGraphicsPipelineCreateInfo_array_host_to_win32(cons } }
+static inline void convert_VkHeadlessSurfaceCreateInfoEXT_win32_to_host(const VkHeadlessSurfaceCreateInfoEXT32 *in, VkHeadlessSurfaceCreateInfoEXT *out) +{ + if (!in) return; + + out->sType = in->sType; + out->pNext = NULL; + out->flags = in->flags; + if (in->pNext) + FIXME("Unexpected pNext\n"); +} + #ifdef _WIN64 static inline void convert_VkImageCreateInfo_win64_to_host(struct conversion_context *ctx, const VkImageCreateInfo *in, VkImageCreateInfo *out) { @@ -46136,6 +46154,37 @@ static NTSTATUS thunk32_vkCreateGraphicsPipelines(void *args) return STATUS_SUCCESS; }
+#ifdef _WIN64 +static NTSTATUS thunk64_vkCreateHeadlessSurfaceEXT(void *args) +{ + struct vkCreateHeadlessSurfaceEXT_params *params = args; + + TRACE("%p, %p, %p, %p\n", params->instance, params->pCreateInfo, params->pAllocator, params->pSurface); + + params->result = vulkan_instance_from_handle(params->instance)->p_vkCreateHeadlessSurfaceEXT(vulkan_instance_from_handle(params->instance)->host.instance, params->pCreateInfo, NULL, params->pSurface); + return STATUS_SUCCESS; +} +#endif /* _WIN64 */ + +static NTSTATUS thunk32_vkCreateHeadlessSurfaceEXT(void *args) +{ + struct + { + PTR32 instance; + PTR32 pCreateInfo; + PTR32 pAllocator; + PTR32 pSurface; + VkResult result; + } *params = args; + VkHeadlessSurfaceCreateInfoEXT pCreateInfo_host; + + TRACE("%#x, %#x, %#x, %#x\n", params->instance, params->pCreateInfo, params->pAllocator, params->pSurface); + + convert_VkHeadlessSurfaceCreateInfoEXT_win32_to_host((const VkHeadlessSurfaceCreateInfoEXT32 *)UlongToPtr(params->pCreateInfo), &pCreateInfo_host); + params->result = vulkan_instance_from_handle((VkInstance)UlongToPtr(params->instance))->p_vkCreateHeadlessSurfaceEXT(vulkan_instance_from_handle((VkInstance)UlongToPtr(params->instance))->host.instance, &pCreateInfo_host, NULL, (VkSurfaceKHR *)UlongToPtr(params->pSurface)); + return STATUS_SUCCESS; +} + #ifdef _WIN64 static NTSTATUS thunk64_vkCreateImage(void *args) { @@ -56450,6 +56499,7 @@ static const char * const vk_instance_extensions[] = { "VK_EXT_debug_report", "VK_EXT_debug_utils", + "VK_EXT_headless_surface", "VK_EXT_layer_settings", "VK_EXT_surface_maintenance1", "VK_EXT_swapchain_colorspace", @@ -56861,6 +56911,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = thunk64_vkCreateFence, thunk64_vkCreateFramebuffer, thunk64_vkCreateGraphicsPipelines, + thunk64_vkCreateHeadlessSurfaceEXT, thunk64_vkCreateImage, thunk64_vkCreateImageView, thunk64_vkCreateIndirectCommandsLayoutEXT, @@ -57510,6 +57561,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = thunk32_vkCreateFence, thunk32_vkCreateFramebuffer, thunk32_vkCreateGraphicsPipelines, + thunk32_vkCreateHeadlessSurfaceEXT, thunk32_vkCreateImage, thunk32_vkCreateImageView, thunk32_vkCreateIndirectCommandsLayoutEXT, diff --git a/include/wine/vulkan.h b/include/wine/vulkan.h index fc729251b44..4384fe49e22 100644 --- a/include/wine/vulkan.h +++ b/include/wine/vulkan.h @@ -415,6 +415,8 @@ #define VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME "VK_KHR_uniform_buffer_standard_layout" #define VK_EXT_PROVOKING_VERTEX_SPEC_VERSION 1 #define VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME "VK_EXT_provoking_vertex" +#define VK_EXT_HEADLESS_SURFACE_SPEC_VERSION 1 +#define VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME "VK_EXT_headless_surface" #define VK_KHR_BUFFER_DEVICE_ADDRESS_SPEC_VERSION 1 #define VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME "VK_KHR_buffer_device_address" #define VK_EXT_LINE_RASTERIZATION_SPEC_VERSION 1 @@ -5386,6 +5388,7 @@ typedef enum VkStructureType VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT = 1000254000, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT = 1000254001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_PROPERTIES_EXT = 1000254002, + VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT = 1000256000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES = 1000257000, VK_STRUCTURE_TYPE_BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO = 1000257002, VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO = 1000257003, @@ -8927,6 +8930,13 @@ typedef struct VkHdrVividDynamicMetadataHUAWEI const void *pDynamicMetadata; } VkHdrVividDynamicMetadataHUAWEI;
+typedef struct VkHeadlessSurfaceCreateInfoEXT +{ + VkStructureType sType; + const void *pNext; + VkHeadlessSurfaceCreateFlagsEXT flags; +} VkHeadlessSurfaceCreateInfoEXT; + typedef struct VkHostImageCopyDevicePerformanceQuery { VkStructureType sType; @@ -17973,6 +17983,7 @@ typedef VkResult (VKAPI_PTR *PFN_vkCreateEvent)(VkDevice, const VkEventCreateInf typedef VkResult (VKAPI_PTR *PFN_vkCreateFence)(VkDevice, const VkFenceCreateInfo *, const VkAllocationCallbacks *, VkFence *); typedef VkResult (VKAPI_PTR *PFN_vkCreateFramebuffer)(VkDevice, const VkFramebufferCreateInfo *, const VkAllocationCallbacks *, VkFramebuffer *); typedef VkResult (VKAPI_PTR *PFN_vkCreateGraphicsPipelines)(VkDevice, VkPipelineCache, uint32_t, const VkGraphicsPipelineCreateInfo *, const VkAllocationCallbacks *, VkPipeline *); +typedef VkResult (VKAPI_PTR *PFN_vkCreateHeadlessSurfaceEXT)(VkInstance, const VkHeadlessSurfaceCreateInfoEXT *, const VkAllocationCallbacks *, VkSurfaceKHR *); typedef VkResult (VKAPI_PTR *PFN_vkCreateImage)(VkDevice, const VkImageCreateInfo *, const VkAllocationCallbacks *, VkImage *); typedef VkResult (VKAPI_PTR *PFN_vkCreateImageView)(VkDevice, const VkImageViewCreateInfo *, const VkAllocationCallbacks *, VkImageView *); typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutEXT)(VkDevice, const VkIndirectCommandsLayoutCreateInfoEXT *, const VkAllocationCallbacks *, VkIndirectCommandsLayoutEXT *); @@ -18615,6 +18626,7 @@ VkResult VKAPI_CALL vkCreateEvent(VkDevice device, const VkEventCreateInfo *pCre VkResult VKAPI_CALL vkCreateFence(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence); VkResult VKAPI_CALL vkCreateFramebuffer(VkDevice device, const VkFramebufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer); VkResult VKAPI_CALL vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines); +VkResult VKAPI_CALL vkCreateHeadlessSurfaceEXT(VkInstance instance, const VkHeadlessSurfaceCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface); VkResult VKAPI_CALL vkCreateImage(VkDevice device, const VkImageCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImage *pImage); VkResult VKAPI_CALL vkCreateImageView(VkDevice device, const VkImageViewCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImageView *pView); VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutEXT(VkDevice device, const VkIndirectCommandsLayoutCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkIndirectCommandsLayoutEXT *pIndirectCommandsLayout); @@ -19506,6 +19518,7 @@ VkResult VKAPI_CALL vkWriteMicromapsPropertiesEXT(VkDevice device, uint32_t micr #define ALL_VK_INSTANCE_FUNCS \ USE_VK_FUNC(vkCreateDebugReportCallbackEXT) \ USE_VK_FUNC(vkCreateDebugUtilsMessengerEXT) \ + USE_VK_FUNC(vkCreateHeadlessSurfaceEXT) \ USE_VK_FUNC(vkCreateWin32SurfaceKHR) \ USE_VK_FUNC(vkDebugReportMessageEXT) \ USE_VK_FUNC(vkDestroyDebugReportCallbackEXT) \
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/vulkan.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 6d8a5ef1b29..a605a8a2374 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -509,10 +509,12 @@ static struct vulkan_funcs vulkan_funcs = .p_get_host_surface_extension = win32u_get_host_surface_extension, };
+ static VkResult nulldrv_vulkan_surface_create( HWND hwnd, VkInstance instance, VkSurfaceKHR *surface, void **private ) { - FIXME( "stub!\n" ); - return VK_ERROR_INCOMPATIBLE_DRIVER; + PFN_vkCreateHeadlessSurfaceEXT p_vkCreateHeadlessSurfaceEXT = (PFN_vkCreateHeadlessSurfaceEXT)p_vkGetInstanceProcAddr( instance, "vkCreateHeadlessSurfaceEXT" ); + VkHeadlessSurfaceCreateInfoEXT create_info = {.sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT}; + return p_vkCreateHeadlessSurfaceEXT( instance, &create_info, NULL, surface ); }
static void nulldrv_vulkan_surface_destroy( HWND hwnd, void *private ) @@ -538,7 +540,7 @@ static VkBool32 nulldrv_vkGetPhysicalDeviceWin32PresentationSupportKHR( VkPhysic
static const char *nulldrv_get_host_surface_extension(void) { - return "VK_WINE_nulldrv_surface"; + return "VK_EXT_headless_surface"; }
static const struct vulkan_driver_funcs nulldrv_funcs =
v2: Query the `vkCreateHeadlessSurfaceEXT` function pointer from the instance before creating surfaces.
On Mon Feb 24 16:15:59 2025 +0000, Rémi Bernon wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/7412/diffs?diff_id=159998&start_sha=e5d603ed3cd095515b5c9db35b56d6dff4282986#b25729bb1124b7420380f2d759cf4d27db2715cc_512_512)
We could pass the instance as `vulkan_instance` struct to drivers.
On Tue Feb 25 12:20:58 2025 +0000, Jacek Caban wrote:
We could pass the instance as `vulkan_instance` struct to drivers.
I'm not sure to see how useful it would be. We only have function pointers for the extensions we expose on the Win32 side, and so no driver could really benefit from it except the null driver which uses headless extension which we also (probably safely?) expose.
Jacek Caban (@jacek) commented about dlls/winevulkan/make_vulkan:
UNSUPPORTED_EXTENSIONS = [ # Instance extensions
- "VK_EXT_headless_surface", # Needs WSI work.
This still needs WSI work, with this patch we'd expose host handle to win32 applications and surface functions will attempt to unwrap it. We should either add `vkCreateHeadlessSurfaceEXT` to `USER_DRIVER_FUNCS` or `"VK_EXT_headless_surface"` to `UNEXPOSED_EXTENSIONS`.
On Tue Feb 25 12:28:02 2025 +0000, Rémi Bernon wrote:
I'm not sure to see how useful it would be. We only have function pointers for the extensions we expose on the Win32 side, and so no driver could really benefit from it except the null driver which uses headless extension which we also (probably safely?) expose.
Well, as you said, null driver would benefit. Is there a reason not to do that?
On Tue Feb 25 12:46:53 2025 +0000, Jacek Caban wrote:
Well, as you said, null driver would benefit. Is there a reason not to do that?
Well it doesn't seem very useful to me to add a parameter that only one callee over 5 will need when it saves only one line of code, and/or a vkGetInstanceProcAddress call in a place that's not really performance critical anyway.
On Tue Feb 25 13:08:22 2025 +0000, Rémi Bernon wrote:
Well it doesn't seem very useful to me to add a parameter that only one callee over 5 will need when it saves only one line of code, and/or a vkGetInstanceProcAddress call in a place that's not really performance critical anyway.
Hmm I wasn't thinking straight sorry, we can probably change the parameter type indeed and get the host handle in the drivers. I'll do that.