From 146b3d08034a06f20b86254ad074e0136261a4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 22 Nov 2024 12:12:19 +0100 Subject: [PATCH] vrclient: Initialize OpenVR registry vulkan extensions. CW-Bug-Id: #24510 --- vrclient_x64/gen_wrapper.py | 1 + vrclient_x64/unix_private.h | 1 + vrclient_x64/unixlib.cpp | 237 ++++++++++++++++++++++++++++- vrclient_x64/unixlib.h | 6 + vrclient_x64/unixlib_generated.cpp | 1 + vrclient_x64/unixlib_generated.h | 1 + vrclient_x64/vrclient_main.c | 50 +++++- vrclient_x64/vrclient_structs.h | 4 + 8 files changed, 296 insertions(+), 5 deletions(-) diff --git a/vrclient_x64/gen_wrapper.py b/vrclient_x64/gen_wrapper.py index e03ba697..bb991788 100755 --- a/vrclient_x64/gen_wrapper.py +++ b/vrclient_x64/gen_wrapper.py @@ -222,6 +222,7 @@ unique_structs = [] UNIX_FUNCS = [ 'vrclient_init', + 'vrclient_init_registry', 'vrclient_HmdSystemFactory', 'vrclient_VRClientCoreFactory', 'vrclient_unload', diff --git a/vrclient_x64/unix_private.h b/vrclient_x64/unix_private.h index da4ebd7a..777d8431 100644 --- a/vrclient_x64/unix_private.h +++ b/vrclient_x64/unix_private.h @@ -26,6 +26,7 @@ extern char *json_convert_startup_info(const char *startup_info); extern char *json_convert_paths(const char *input); extern NTSTATUS vrclient_init( void *args ); +extern NTSTATUS vrclient_init_registry( void *args ); extern NTSTATUS vrclient_unload( void *args ); extern NTSTATUS vrclient_HmdSystemFactory( void *args ); extern NTSTATUS vrclient_VRClientCoreFactory( void *args ); diff --git a/vrclient_x64/unixlib.cpp b/vrclient_x64/unixlib.cpp index 0b80313c..b61182d4 100644 --- a/vrclient_x64/unixlib.cpp +++ b/vrclient_x64/unixlib.cpp @@ -2,6 +2,10 @@ #include #include +#include + +#define WINE_VK_HOST +#include #if 0 #pragma makedep unix @@ -18,6 +22,11 @@ VkPhysicalDevice_T *(*p_get_native_VkPhysicalDevice)( VkPhysicalDevice_T * ); VkPhysicalDevice_T *(*p_get_wrapped_VkPhysicalDevice)( VkInstance_T *, VkPhysicalDevice_T * ); VkQueue_T *(*p_get_native_VkQueue)( VkQueue_T * ); +static PFN_vkCreateInstance p_vkCreateInstance; +static PFN_vkDestroyInstance p_vkDestroyInstance; +static PFN_vkEnumeratePhysicalDevices p_vkEnumeratePhysicalDevices; +static PFN_vkGetPhysicalDeviceProperties p_vkGetPhysicalDeviceProperties; + static void *get_winevulkan_unixlib( HMODULE winevulkan ) { UINT64 unix_funcs; @@ -42,6 +51,8 @@ static void *get_winevulkan_unixlib( HMODULE winevulkan ) return dlopen( info.dli_fname, RTLD_NOW ); } +static void *vulkan; + static BOOL load_vk_unwrappers( HMODULE winevulkan ) { void *unix_handle; @@ -56,7 +67,8 @@ static BOOL load_vk_unwrappers( HMODULE winevulkan ) if (!(p_##name = (decltype(p_##name))dlsym( unix_handle, "__wine_" #name ))) \ { \ ERR( "%s not found.\n", #name ); \ - return FALSE; \ + dlclose( unix_handle ); \ + return FALSE; \ } LOAD_FUNC( get_native_VkDevice ) @@ -67,12 +79,230 @@ static BOOL load_vk_unwrappers( HMODULE winevulkan ) #undef LOAD_FUNC - dlclose(unix_handle); + dlclose( unix_handle ); + return TRUE; +} + +static BOOL load_vulkan(void) +{ + if (!(vulkan = dlopen( "libvulkan.so.1", RTLD_NOW )) && + !(vulkan = dlopen( "libvulkan.so", RTLD_NOW ))) + { + ERR( "Unable to open libvulkan.so.1\n" ); + return FALSE; + } + +#define LOAD_FUNC( name ) \ + if (!(p_##name = (decltype(p_##name))dlsym( vulkan, #name ))) \ + { \ + ERR( "%s not found.\n", #name ); \ + dlclose( vulkan ); \ + return FALSE; \ + } + + LOAD_FUNC( vkCreateInstance ) + LOAD_FUNC( vkDestroyInstance ) + LOAD_FUNC( vkEnumeratePhysicalDevices ) + LOAD_FUNC( vkGetPhysicalDeviceProperties ) + +#undef LOAD_FUNC + return TRUE; } static void *vrclient; +static UINT asciiz_to_unicode( WCHAR *dst, const char *src ) +{ + WCHAR *p = dst; + while ((*p++ = *src++)); + return (p - dst) * sizeof(WCHAR); +} + +static BOOL set_reg_value( HKEY hkey, const WCHAR *name, UINT type, const void *value, DWORD count ) +{ + unsigned short name_size = name ? lstrlenW( name ) * sizeof(WCHAR) : 0; + UNICODE_STRING nameW = { name_size, name_size, (WCHAR *)name }; + return !NtSetValueKey( hkey, &nameW, 0, type, value, count ); +} + +static void set_reg_ascii_dword( HKEY hkey, const char *name, DWORD value ) +{ + WCHAR nameW[64], valueW[128]; + asciiz_to_unicode( nameW, name ); + set_reg_value( hkey, nameW, REG_DWORD, &value, sizeof(value) ); +} + +static void set_reg_ascii_str( HKEY hkey, const char *name, const char *value ) +{ + size_t len = strlen( value ) + 1; + WCHAR nameW[64], *valueW; + + if (!(valueW = (WCHAR *)malloc( len * sizeof(*valueW) ))) return; + asciiz_to_unicode( valueW, value ); + asciiz_to_unicode( nameW, name ); + set_reg_value( hkey, nameW, REG_SZ, valueW, len * sizeof(*valueW) ); + free( valueW ); +} + +static int parse_extensions( const char *extensions, char ***list ) +{ + uint32_t count = 0, o = 0; + char *ptr, *str, **entry; + + *list = NULL; + if (!(ptr = str = strdup( extensions ))) return 0; + for (ptr = str; *ptr;) if (*ptr++ == ' ') count++; + if (ptr != str) count++; + + *list = entry = (char **)malloc( (count + 1) * sizeof(*list) ); + for (*entry++ = ptr = str; *ptr;) if (*ptr++ == ' ') { ptr[-1] = 0; *entry++ = ptr; } + *entry = NULL; + + return count; +} + +NTSTATUS vrclient_init_registry( void *args ) +{ + struct vrclient_init_registry_params *params = (struct vrclient_init_registry_params *)args; + VkApplicationInfo app_info = + { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pApplicationName = "proton_vrhelper", + .applicationVersion = 1, + .pEngineName = "proton_vrhelper", + .engineVersion = 1, + .apiVersion = VK_MAKE_VERSION(1, 1, 0), + }; + VkInstanceCreateInfo inst_info = + { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &app_info, + }; + + uint32_t error, instance_extensions_count, device_count; + u_IVRClientCore_IVRClientCore_003 *client_core; + u_IVRCompositor_IVRCompositor_028 *compositor; + VkPhysicalDevice *phys_devices = NULL; + VkPhysicalDeviceProperties prop = {}; + char *buffer = NULL, *new_buffer; + char *xr_inst_ext, *xr_dev_ext; + VkInstance vk_instance = NULL; + BOOL vr_initialized = FALSE; + char **instance_extensions; + HMODULE hvulkan = NULL; + DWORD vr_status = ~0u; + unsigned int length; + HMODULE hwineopenxr; + void *lib_vrclient; + void *unix_handle; + DWORD hmd_present; + int return_code; + unsigned int i; + VkResult res; + + TRACE( "Starting VR info initialization.\n" ); + + if (!load_vulkan()) return 0; + + if (!(client_core = (u_IVRClientCore_IVRClientCore_003 *)p_VRClientCoreFactory( "IVRClientCore_003", &return_code ))) + { + ERR( "Could not get IVRClientCore, error %d.\n", return_code ); + goto failed; + } + + /* Without overriding the app_key vrclient waits 2 seconds for a valid appkey before returning. */ + if ((error = client_core->Init( VRApplication_Background, NULL ))) + { + if (error == VRInitError_Init_NoServerForBackgroundApp) TRACE( "VR server is not available.\n" ); + else ERR( "VR init failed, error %u.\n", error ); + client_core = NULL; + goto failed; + } + + hmd_present = !!client_core->BIsHmdPresent(); + TRACE( "hmd_present %#x.\n", hmd_present ); + set_reg_ascii_dword( params->vr_key, "is_hmd_present", hmd_present ); + + if (!(compositor = (u_IVRCompositor_IVRCompositor_028 *)client_core->GetGenericInterface( "IVRCompositor_028", &error ))) + { + ERR( "Could not get compositor, error %u.\n", error ); + goto failed; + } + + length = compositor->GetVulkanInstanceExtensionsRequired( nullptr, 0 ); + if (!(buffer = (char *)malloc( length ))) goto failed; + *buffer = 0; + compositor->GetVulkanInstanceExtensionsRequired( buffer, length ); + TRACE( "Instance extensions %s.\n", buffer ); + set_reg_ascii_str( params->vr_key, "openvr_vulkan_instance_extensions", buffer ); + + inst_info.enabledExtensionCount = parse_extensions( buffer, &instance_extensions ); + inst_info.ppEnabledExtensionNames = instance_extensions; + res = p_vkCreateInstance( &inst_info, NULL, &vk_instance ); + if (instance_extensions) free( instance_extensions[0] ); + free( instance_extensions ); + free( buffer ); + + if (res != VK_SUCCESS) + { + ERR( "Could not create VK instance, res %d.\n", res ); + goto failed; + } + + if ((res = p_vkEnumeratePhysicalDevices( vk_instance, &device_count, NULL )) != VK_SUCCESS) + { + ERR( "Could not enumerate physical devices, res %d.\n", res ); + goto failed; + } + if (!(phys_devices = (VkPhysicalDevice *)malloc( device_count * sizeof(*phys_devices) ))) goto failed; + if ((res = p_vkEnumeratePhysicalDevices( vk_instance, &device_count, phys_devices )) != VK_SUCCESS) + { + ERR( "Could not enumerate physical devices, res %d.\n", res ); + goto failed; + } + + for (i = 0; i < device_count; ++i) + { + char name[256], *buffer; + + p_vkGetPhysicalDeviceProperties( phys_devices[i], &prop ); + if (prop.apiVersion < VK_MAKE_VERSION( 1, 1, 0 )) + { + TRACE( "Skipping Vulkan 1.0 adapter %s.\n", prop.deviceName ); + continue; + } + + length = compositor->GetVulkanDeviceExtensionsRequired( phys_devices[i], nullptr, 0 ); + if (!(buffer = (char *)malloc( length ))) goto failed; + *buffer = 0; + compositor->GetVulkanDeviceExtensionsRequired( phys_devices[i], buffer, length ); + + sprintf( name, "PCIID:%04x:%04x", prop.vendorID, prop.deviceID ); + TRACE( "%s: %s.\n", name, buffer ); + set_reg_ascii_str( params->vr_key, name, buffer ); + + free( buffer ); + } + + free( phys_devices ); + p_vkDestroyInstance( vk_instance, NULL ); + + client_core->Cleanup(); + params->_ret = true; + dlclose( vulkan ); + vulkan = NULL; + return 0; + +failed: + free( phys_devices ); + if (vk_instance) p_vkDestroyInstance( vk_instance, NULL ); + if (client_core) client_core->Cleanup(); + dlclose( vulkan ); + vulkan = NULL; + return 0; +} + NTSTATUS vrclient_init( void *args ) { struct vrclient_init_params *params = (struct vrclient_init_params *)args; @@ -103,8 +333,7 @@ NTSTATUS vrclient_init( void *args ) #undef LOAD_FUNC - if (!load_vk_unwrappers( params->winevulkan )) - return 0; + if (!load_vk_unwrappers( params->winevulkan )) return 0; params->_ret = true; return 0; diff --git a/vrclient_x64/unixlib.h b/vrclient_x64/unixlib.h index d6ad96ea..af9c2428 100644 --- a/vrclient_x64/unixlib.h +++ b/vrclient_x64/unixlib.h @@ -53,6 +53,12 @@ struct vrclient_init_params char *unix_path; }; +struct vrclient_init_registry_params +{ + int8_t _ret; + HKEY vr_key; +}; + struct vrclient_HmdSystemFactory_params { void *_ret; diff --git a/vrclient_x64/unixlib_generated.cpp b/vrclient_x64/unixlib_generated.cpp index e9b7e761..0d7823f6 100644 --- a/vrclient_x64/unixlib_generated.cpp +++ b/vrclient_x64/unixlib_generated.cpp @@ -8,6 +8,7 @@ extern "C" const unixlib_entry_t __wine_unix_call_funcs[] = { vrclient_init, + vrclient_init_registry, vrclient_HmdSystemFactory, vrclient_VRClientCoreFactory, vrclient_unload, diff --git a/vrclient_x64/unixlib_generated.h b/vrclient_x64/unixlib_generated.h index 71459df1..27056f23 100644 --- a/vrclient_x64/unixlib_generated.h +++ b/vrclient_x64/unixlib_generated.h @@ -29222,6 +29222,7 @@ struct IVRTrackedCamera_IVRTrackedCamera_006_GetCameraTrackingSpace_params enum unix_funcs { unix_vrclient_init, + unix_vrclient_init_registry, unix_vrclient_HmdSystemFactory, unix_vrclient_VRClientCoreFactory, unix_vrclient_unload, diff --git a/vrclient_x64/vrclient_main.c b/vrclient_x64/vrclient_main.c index cc7e8556..46a08b7c 100644 --- a/vrclient_x64/vrclient_main.c +++ b/vrclient_x64/vrclient_main.c @@ -208,10 +208,49 @@ static BOOL set_vr_status( HKEY key, DWORD value ) return TRUE; } +static HMODULE vrclient; + +static DWORD WINAPI initialize_vr_data( void *arg ) +{ + struct vrclient_init_registry_params params = {.vr_key = arg}; + HKEY vr_key = arg; + HMODULE openxr; + + VRCLIENT_CALL( vrclient_init_registry, ¶ms ); + + if (!params._ret) + { + ERR( "Failed to initialize VR info.\n" ); + set_vr_status( vr_key, -1 ); + } + else + { + if (!(openxr = LoadLibraryW( L"wineopenxr" ))) + WARN( "Could not load wineopenxr, err %u.\n", GetLastError() ); + else + { + BOOL (CDECL * init)(void); + + if ((init = (void *)GetProcAddress( openxr, "wineopenxr_init_registry" ))) init(); + else ERR( "Failed to find wineopenxr_init_registry export\n" ); + + FreeLibrary( openxr ); + } + + set_vr_status( vr_key, 1 ); + WINE_TRACE( "Completed VR info initialization.\n" ); + } + + RegCloseKey( vr_key ); + + FreeLibraryAndExitThread( vrclient, 0 ); +} + BOOL CDECL vrclient_init_registry(void) { WCHAR pathW[PATH_MAX]; LSTATUS status; + HANDLE thread; HKEY vr_key; DWORD disp; @@ -261,8 +300,17 @@ BOOL CDECL vrclient_init_registry(void) return FALSE; } + GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (void *)initialize_vr_data, &vrclient ); + if (!(thread = CreateThread( NULL, 0, initialize_vr_data, vr_key, 0, NULL ))) + { + WINE_ERR( "Could not create thread, error %u.\n", GetLastError() ); + FreeLibrary( vrclient ); + RegCloseKey( vr_key ); + return FALSE; + } + CloseHandle( thread ); + TRACE( "Initialized OpenVR registry entries\n" ); - RegCloseKey( vr_key ); return TRUE; } diff --git a/vrclient_x64/vrclient_structs.h b/vrclient_x64/vrclient_structs.h index fec05164..10c11bc3 100644 --- a/vrclient_x64/vrclient_structs.h +++ b/vrclient_x64/vrclient_structs.h @@ -101,6 +101,10 @@ typedef struct VkQueue_T VkQueue_T; #define VRRenderModelError_InvalidArg 300 #define VRRenderModelError_InvalidTexture 400 +#define VRApplication_Background 3 + +#define VRInitError_Init_NoServerForBackgroundApp 121 + #define VRCompositorTimingMode_Explicit_ApplicationPerformsPostPresentHandoff 2 enum EVRSubmitFlags