From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/main.c | 21 +++++- dlls/win32u/ntuser_private.h | 3 + dlls/win32u/syscall.c | 13 +++- dlls/win32u/sysparams.c | 3 + dlls/win32u/unixlib.h | 38 +++++++++++ dlls/win32u/vulkan.c | 125 +++++++++++++++++++++++++++++++++-- include/ntuser.h | 1 + include/wine/vulkan_driver.h | 4 ++ 8 files changed, 199 insertions(+), 9 deletions(-) create mode 100644 dlls/win32u/unixlib.h
diff --git a/dlls/win32u/main.c b/dlls/win32u/main.c index 193c2c048bf..c5349266028 100644 --- a/dlls/win32u/main.c +++ b/dlls/win32u/main.c @@ -31,6 +31,8 @@ #include "wine/asm.h" #include "win32syscalls.h"
+#include "unixlib.h" + void *__wine_syscall_dispatcher = NULL;
/******************************************************************* @@ -2554,8 +2556,25 @@ ALL_SYSCALLS
void *dummy = NtQueryVirtualMemory; /* forced import to avoid link error with winecrt0 */
+static void set_thread_name( const WCHAR *name ) +{ + THREAD_NAME_INFORMATION info; + RtlInitUnicodeString( &info.ThreadName, name ); + NtSetInformationThread( GetCurrentThread(), ThreadNameInformation, &info, sizeof(info) ); +} + +static NTSTATUS WINAPI timeline_thread( void *arg ) +{ + set_thread_name( L"wine_timeline_thread" ); + RtlExitUserThread( NtUserCallNoParam( NtUserCallNoParam_TimelineThread ) ); +} + BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved ) { + struct process_attach_params params = + { + .timeline_thread_proc = (UINT_PTR)timeline_thread, + }; HMODULE ntdll; void **dispatcher_ptr; const UNICODE_STRING ntdll_name = RTL_CONSTANT_STRING( L"ntdll.dll" ); @@ -2568,7 +2587,7 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved ) LdrGetDllHandle( NULL, 0, &ntdll_name, &ntdll ); dispatcher_ptr = RtlFindExportedRoutineByName( ntdll, "__wine_syscall_dispatcher" ); __wine_syscall_dispatcher = *dispatcher_ptr; - if (!__wine_init_unix_call()) WINE_UNIX_CALL( 0, NULL ); + if (!__wine_init_unix_call()) WINE_UNIX_CALL( unix_process_attach, ¶ms ); break; } return TRUE; diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index c38b6d0e029..2265f6f873e 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -222,6 +222,9 @@ extern LRESULT system_tray_call( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar extern BOOL set_dc_pixel_format_internal( HDC hdc, int format, struct opengl_drawable **drawable );
/* vulkan.c */ +extern PRTL_THREAD_START_ROUTINE timeline_thread_proc; +extern NTSTATUS timeline_thread(void); + extern struct vulkan_instance *vulkan_instance_create( const struct vulkan_instance_extensions *extensions );
/* window.c */ diff --git a/dlls/win32u/syscall.c b/dlls/win32u/syscall.c index 59913497915..352c496660e 100644 --- a/dlls/win32u/syscall.c +++ b/dlls/win32u/syscall.c @@ -34,6 +34,8 @@ #include "wine/unixlib.h" #include "win32syscalls.h"
+#include "unixlib.h" + ULONG_PTR zero_bits = 0;
static void stub_syscall( const char *name ) @@ -89,8 +91,14 @@ static const char *usercall_names[NtUserCallCount] = #undef USER32_CALLBACK_ENTRY };
-static NTSTATUS init( void *args ) +static PRTL_THREAD_START_ROUTINE get_thread_start_routine( UINT64 value ) +{ + return (PRTL_THREAD_START_ROUTINE)(UINT_PTR)value; +} + +static NTSTATUS process_attach( void *args ) { + struct process_attach_params *params = args; #ifdef _WIN64 if (NtCurrentTeb()->WowTebOffset) { @@ -102,10 +110,11 @@ static NTSTATUS init( void *args ) #endif KeAddSystemServiceTable( syscalls, NULL, ARRAY_SIZE(syscalls), arguments, 1 ); ntdll_add_syscall_debug_info( 1, syscall_names, usercall_names ); + timeline_thread_proc = get_thread_start_routine( params->timeline_thread_proc ); return STATUS_SUCCESS; }
const unixlib_entry_t __wine_unix_call_funcs[] = { - init, + [unix_process_attach] = process_attach, }; diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 536a417bd87..8f7f71d6391 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -7444,6 +7444,9 @@ ULONG_PTR WINAPI NtUserCallNoParam( ULONG code ) display_mode_changed( FALSE ); return TRUE;
+ case NtUserCallNoParam_TimelineThread: + return timeline_thread(); + /* temporary exports */ case NtUserExitingThread: exiting_thread_id = GetCurrentThreadId(); diff --git a/dlls/win32u/unixlib.h b/dlls/win32u/unixlib.h new file mode 100644 index 00000000000..e1f063abea8 --- /dev/null +++ b/dlls/win32u/unixlib.h @@ -0,0 +1,38 @@ +/* + * Copyright 2021 Rémi Bernon 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 __WIN32U_UNIXLIB_H +#define __WIN32U_UNIXLIB_H + +#include <stdarg.h> + +#include <windef.h> +#include <winbase.h> +#include <winternl.h> + +struct process_attach_params +{ + UINT64 timeline_thread_proc; /* PE-side function pointer */ +}; + +enum unix_funcs +{ + unix_process_attach, +}; + +#endif /* __WIN32U_UNIXLIB_H */ diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index fd34ce8b968..3bcf5d62c35 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -332,6 +332,103 @@ static HANDLE open_shared_resource_from_name( const WCHAR *name ) return open_name.hNtHandle; }
+static VkResult timeline_semaphore_create( struct vulkan_device *device, VkSemaphore *semaphore ) +{ + VkSemaphoreTypeCreateInfo type_info = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO}; + VkSemaphoreCreateInfo create_info = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = &type_info}; + type_info.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; + return device->p_vkCreateSemaphore( device->host.device, &create_info, NULL, semaphore ); +} + +static void timeline_semaphore_signal( struct vulkan_device *device, VkSemaphore semaphore, uint64_t value ) +{ + PFN_vkSignalSemaphore p_vkSignalSemaphore = device->p_vkSignalSemaphore ? device->p_vkSignalSemaphore : device->p_vkSignalSemaphoreKHR; + VkSemaphoreSignalInfo info = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO, .semaphore = semaphore, .value = value}; + VkResult res = p_vkSignalSemaphore( device->host.device, &info ); + if (res) ERR( "Failed to signal timeline semaphore %#jx value %#jx, res %d\n", (uintmax_t)semaphore, (uintmax_t)value, res ); +} + +static VkResult timeline_semaphore_wait_any( struct vulkan_device *device, uint32_t count, VkSemaphore *semaphores, uint64_t *values, uint64_t timeout ) +{ + PFN_vkWaitSemaphores p_vkWaitSemaphores = device->p_vkWaitSemaphores ? device->p_vkWaitSemaphores : device->p_vkWaitSemaphoresKHR; + VkSemaphoreWaitInfo wait_info = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, .flags = VK_SEMAPHORE_WAIT_ANY_BIT, .semaphoreCount = count, + .pSemaphores = semaphores, .pValues = values}; + VkResult res = p_vkWaitSemaphores( device->host.device, &wait_info, timeout ); + if (res && res != VK_TIMEOUT) ERR( "Failed to wait on %u timeline semaphores, res %d\n", count, res ); + return res; +} + +static pthread_mutex_t timeline_lock = PTHREAD_MUTEX_INITIALIZER; +static struct vulkan_device *timeline_thread_param; +PRTL_THREAD_START_ROUTINE timeline_thread_proc; + +NTSTATUS timeline_thread(void) +{ + struct vulkan_device *device = timeline_thread_param; + struct mempool pool = {0}; + VkResult res; + + TRACE( "Timeline thread running for device %p\n", device ); + + TRACE( "Signaling timeline %p/%#jx\n", device, (uintmax_t)device->timeline_value ); + timeline_semaphore_signal( device, device->timeline_semaphore, device->timeline_value++ ); + + do + { + VkSemaphore *wait_semaphores, *wait_semaphore; + uint64_t *wait_values, *wait_value; + uint32_t count = 0; + + pthread_mutex_lock( &timeline_lock ); + + TRACE( "Waiting on semaphores:\n" ); + wait_semaphores = wait_semaphore = mem_alloc( &pool, (count * 2 + 1) * sizeof(*wait_semaphores) ); + wait_values = wait_value = mem_alloc( &pool, (count * 2 + 1) * sizeof(*wait_values) ); + + TRACE( " - timeline %p/%#jx\n", device, (uintmax_t)device->timeline_value ); + *wait_semaphore++ = device->timeline_semaphore; + *wait_value++ = device->timeline_value; + count = wait_semaphore - wait_semaphores; + + pthread_mutex_unlock( &timeline_lock ); + + res = timeline_semaphore_wait_any( device, count, wait_semaphores, wait_values, -1 ); + mem_free( &pool ); + } while (!res && InterlockedCompareExchangePointer( &device->timeline_thread, NULL, NULL )); + + if (res) ERR( "Timeline thread for device %p exiting with res %d\n", device, res ); + return 0; +} + +static void timeline_thread_notify( struct vulkan_device *device ) +{ + NTSTATUS status; + + pthread_mutex_lock( &timeline_lock ); + + if (!device->timeline_semaphore) timeline_semaphore_create( device, &device->timeline_semaphore ); + if (!device->timeline_semaphore) ERR( "Failed to create device timeline semaphore\n" ); + + TRACE( "Signaling timeline %p/%#jx\n", device, (uintmax_t)device->timeline_value ); + timeline_semaphore_signal( device, device->timeline_semaphore, device->timeline_value++ ); + + if (!device->timeline_thread) + { + timeline_thread_param = device; + + status = NtCreateThreadEx( &device->timeline_thread, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), + timeline_thread_proc, NULL, 0, 0, 0, 0, NULL ); + if (status) ERR( "Failed to start timeline thread, status %#x\n", status ); + + TRACE( "Waiting on timeline %p/%#jx\n", device, (uintmax_t)device->timeline_value ); + timeline_semaphore_wait_any( device, 1, &device->timeline_semaphore, &device->timeline_value, -1 ); + + timeline_thread_param = NULL; + } + + pthread_mutex_unlock( &timeline_lock ); +} + static const void *find_next_struct( const VkBaseInStructure *header, VkStructureType type ) { for (; header; header = header->pNext) if (header->sType == type) return header; @@ -781,6 +878,7 @@ static VkResult win32u_vkCreateDevice( VkPhysicalDevice client_physical_device,
if (!(device = calloc( 1, offsetof(struct vulkan_device, queues[queue_count]) ))) return VK_ERROR_OUT_OF_HOST_MEMORY; device->extensions = client_device->extensions; + device->timeline_value = 1;
if ((res = convert_device_create_info( physical_device, create_info, &pool, device ))) goto failed; if ((res = instance->p_vkCreateDevice( physical_device->host.physical_device, create_info, NULL /* allocator */, &host_device ))) goto failed; @@ -816,9 +914,16 @@ static void win32u_vkDestroyDevice( VkDevice client_device, const VkAllocationCa struct vulkan_device *device = vulkan_device_from_handle( client_device ); struct vulkan_instance *instance = device->physical_device->instance; unsigned int i; + HANDLE thread;
if (!device) return;
+ if ((thread = InterlockedExchangePointer( &device->timeline_thread, NULL ))) + { + timeline_semaphore_signal( device, device->timeline_semaphore, device->timeline_value++ ); + NtWaitForSingleObject( thread, FALSE, NULL ); + } + device->p_vkDestroyDevice( device->host.device, NULL /* pAllocator */ ); for (i = 0; i < device->queue_count; i++) instance->p_remove_object( instance, &device->queues[i].obj ); @@ -946,12 +1051,9 @@ static VkResult win32u_vkAllocateMemory( VkDevice client_device, const VkMemoryA
if (device->client.device->extensions.has_VK_KHR_win32_keyed_mutex && memory->sync) { - VkSemaphoreTypeCreateInfo semaphore_type = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO}; - VkSemaphoreCreateInfo semaphore_create = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = &semaphore_type}; VkImportSemaphoreFdInfoKHR fd_info = {.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR};
- semaphore_type.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; - if ((res = device->p_vkCreateSemaphore( device->host.device, &semaphore_create, NULL, &memory->semaphore ))) goto failed; + if ((res = timeline_semaphore_create( device, &memory->semaphore ))) goto failed;
fd_info.handleType = get_host_external_semaphore_type(); fd_info.semaphore = memory->semaphore; @@ -2096,6 +2198,7 @@ static VkResult win32u_vkQueueBindSparse( VkQueue client_queue, uint32_t count, struct vulkan_fence *fence = client_fence ? vulkan_fence_from_handle( client_fence ) : NULL; struct vulkan_queue *queue = vulkan_queue_from_handle( client_queue ); struct vulkan_device *device = queue->device; + VkResult res;
TRACE( "queue %p, count %u, binds %p, fence %p\n", queue, count, binds, fence );
@@ -2161,7 +2264,10 @@ static VkResult win32u_vkQueueBindSparse( VkQueue client_queue, uint32_t count, } }
- return device->p_vkQueueBindSparse( queue->host.queue, count, binds, fence ? fence->host.fence : 0 ); + res = device->p_vkQueueBindSparse( queue->host.queue, count, binds, fence ? fence->host.fence : 0 ); + if (!res) timeline_thread_notify( queue->device ); + + return res; }
static VkResult win32u_vkQueueSubmit( VkQueue client_queue, uint32_t count, const VkSubmitInfo *submits, VkFence client_fence ) @@ -2312,6 +2418,7 @@ static VkResult win32u_vkQueueSubmit( VkQueue client_queue, uint32_t count, cons }
res = device->p_vkQueueSubmit( queue->host.queue, count, submits, fence ? fence->host.fence : 0 ); + if (!res) timeline_thread_notify( queue->device );
failed: mem_free( &pool ); @@ -2375,6 +2482,7 @@ static VkResult queue_submit( struct vulkan_queue *queue, uint32_t count, const }
res = p_vkQueueSubmit2( queue->host.queue, count, submits, fence ? fence->host.fence : 0 ); + if (!res) timeline_thread_notify( queue->device );
failed: mem_free( &pool ); @@ -2564,12 +2672,16 @@ static VkResult signal_semaphore( struct vulkan_device *device, const VkSemaphor { struct semaphore *semaphore = semaphore_from_handle( signal_info->semaphore ); VkSemaphoreSignalInfo info = *signal_info; + VkResult res;
if (info.pNext) FIXME( "pNext not implemented\n" ); info.pNext = NULL;
info.semaphore = semaphore->obj.host.semaphore; - return p_vkSignalSemaphore( device->host.device, &info ); + res = p_vkSignalSemaphore( device->host.device, &info ); + if (!res) timeline_thread_notify( device ); + + return res; }
static VkResult win32u_vkSignalSemaphore( VkDevice client_device, const VkSemaphoreSignalInfo *signal_info ) @@ -2609,6 +2721,7 @@ static VkResult wait_semaphores( struct vulkan_device *device, const VkSemaphore struct vulkan_semaphore *semaphore = vulkan_semaphore_from_handle( semaphores[i] ); semaphores[i] = semaphore->host.semaphore; } + timeline_thread_notify( device );
res = p_vkWaitSemaphores( device->host.device, &info, timeout );
diff --git a/include/ntuser.h b/include/ntuser.h index 664bcfe459d..1c740d98f70 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -1072,6 +1072,7 @@ enum NtUserCallNoParam_GetShellWindow, NtUserCallNoParam_GetTaskmanWindow, NtUserCallNoParam_DisplayModeChanged, + NtUserCallNoParam_TimelineThread, /* temporary exports */ NtUserExitingThread, NtUserThreadDetach, diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h index 6e37a462ef2..1dbaed13f1f 100644 --- a/include/wine/vulkan_driver.h +++ b/include/wine/vulkan_driver.h @@ -212,6 +212,10 @@ struct vulkan_device ALL_VK_DEVICE_FUNCS #undef USE_VK_FUNC
+ HANDLE timeline_thread; + VkSemaphore timeline_semaphore; + uint64_t timeline_value; + uint64_t queue_count; struct vulkan_queue queues[]; };