#include #include #include #define __USE_GNU #include #include #include #include "windef.h" #include "winbase.h" #include "winnls.h" #include "winternl.h" #include "wine/debug.h" #include "initguid.h" #define COBJMACROS #include "d3d11_4.h" #include "dxvk-interop.h" #include "vrclient_defs.h" #include "vrclient_private.h" #include "flatapi.h" #include "cppIVRClientCore_IVRClientCore_003.h" #include "cppIVRCompositor_IVRCompositor_021.h" #include "cppIVRCompositor_IVRCompositor_022.h" #include "wine/unixlib.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) WINE_DEFAULT_DEBUG_CHANNEL(vrclient); static void *vrclient_lib; struct compositor_data compositor_data; BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { TRACE("(%p, %u, %p)\n", instance, reason, reserved); switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(instance); break; case DLL_PROCESS_DETACH: if (vrclient_lib) { if (compositor_data.client_core_linux_side) { cppIVRClientCore_IVRClientCore_003_Cleanup(compositor_data.client_core_linux_side); compositor_data.client_core_linux_side = NULL; } dlclose(vrclient_lib); vrclient_lib = NULL; } break; } return TRUE; } /* returns the number of bytes written to dst, not including the NUL terminator */ unsigned int vrclient_unix_path_to_dos_path(bool api_result, const char *src, char *dst, uint32_t dst_bytes) { WCHAR *dosW; uint32_t r; if(!dst || !dst_bytes) return 0; if(!src || !api_result){ *dst = 0; return 0; } dosW = wine_get_dos_file_name(src); *dst = 0; if(!dosW){ WARN("Unable to convert unix filename to DOS: %s\n", src); return 0; } r = WideCharToMultiByte(CP_ACP, 0, dosW, -1, dst, dst_bytes, NULL, NULL); HeapFree(GetProcessHeap(), 0, dosW); return r == 0 ? 0 : r - 1; } #define IS_ABSOLUTE(x) (*x == '/' || *x == '\\' || (*x && *(x + 1) == ':')) /* returns non-zero on success, zero on failure */ bool vrclient_dos_path_to_unix_path(const char *src, char *dst) { *dst = 0; if(!src || !*src) return 0; if(IS_ABSOLUTE(src)){ /* absolute path, use wine conversion */ WCHAR srcW[PATH_MAX]; char *unix_path; uint32_t r; r = MultiByteToWideChar(CP_UNIXCP, 0, src, -1, srcW, PATH_MAX); if(r == 0) return 0; unix_path = wine_get_unix_file_name(srcW); if(!unix_path){ WARN("Unable to convert DOS filename to unix: %s\n", src); return 0; } if (!realpath(unix_path, dst)) { ERR("Could not get real path for %s.\n", unix_path); lstrcpynA(dst, unix_path, PATH_MAX); } HeapFree(GetProcessHeap(), 0, unix_path); }else{ /* relative path, just fix up backslashes */ const char *s; char *d; for(s = src, d = dst; *src; ++s, ++d){ if(*s == '\\') *d = '/'; else *d = *s; } *d = 0; } return 1; } static BOOL array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size) { SIZE_T max_capacity, new_capacity; void *new_elements; if (count <= *capacity) return TRUE; max_capacity = ~(SIZE_T)0 / size; if (count > max_capacity) return FALSE; new_capacity = max(1, *capacity); while (new_capacity < count && new_capacity <= max_capacity / 2) new_capacity *= 2; if (new_capacity < count) new_capacity = count; if (!*elements) new_elements = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_capacity * size); else new_elements = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *elements, new_capacity * size); if (!new_elements) return FALSE; *elements = new_elements; *capacity = new_capacity; return TRUE; } #include "win_constructors.h" #include "win_destructors.h" typedef void (*pfn_dtor)(struct w_steam_iface *); static const struct { const char *iface_version; struct w_steam_iface *(*ctor)(void *); void (*dtor)(struct w_steam_iface *); } constructors[] = { #include "win_constructors_table.dat" }; struct w_steam_iface *create_win_interface(const char *name, void *linux_side) { unsigned int i; TRACE("trying to create %s\n", name); if(!linux_side) return NULL; for(i = 0; i < sizeof(constructors) / sizeof(*constructors); ++i){ if(!strcmp(name, constructors[i].iface_version)) return constructors[i].ctor(linux_side); } ERR("Don't recognize interface name: %s\n", name); return NULL; } static pfn_dtor get_win_destructor(const char *name) { unsigned int i; for(i = 0; i < sizeof(constructors) / sizeof(*constructors); ++i){ if(!strcmp(name, constructors[i].iface_version)) return constructors[i].dtor; } return NULL; } static void *(*vrclient_HmdSystemFactory)(const char *name, int *return_code); static void *(*vrclient_VRClientCoreFactory)(const char *name, int *return_code); static int load_vrclient(void) { static const WCHAR PROTON_VR_RUNTIME_W[] = {'P','R','O','T','O','N','_','V','R','_','R','U','N','T','I','M','E',0}; WCHAR pathW[PATH_MAX]; char *pathU; DWORD sz; #ifdef _WIN64 static const char append_path[] = "/bin/linux64/vrclient.so"; #else static const char append_path[] = "/bin/vrclient.so"; #endif if(vrclient_lib) return 1; /* PROTON_VR_RUNTIME is provided by the proton setup script */ if(!GetEnvironmentVariableW(PROTON_VR_RUNTIME_W, pathW, ARRAY_SIZE(pathW))) { DWORD type, size; LSTATUS status; HKEY vr_key; if ((status = RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Wine\\VR", 0, KEY_READ, &vr_key))) { WINE_WARN("Could not create key, status %#x.\n", status); return 0; } size = sizeof(pathW); if ((status = RegQueryValueExW(vr_key, PROTON_VR_RUNTIME_W, NULL, &type, (BYTE *)pathW, &size))) { WINE_WARN("Could not query value, status %#x.\n", status); RegCloseKey(vr_key); return 0; } if (type != REG_SZ) { WINE_ERR("Unexpected value type %#x.\n", type); RegCloseKey(vr_key); return 0; } RegCloseKey(vr_key); } sz = WideCharToMultiByte(CP_UNIXCP, 0, pathW, -1, NULL, 0, NULL, NULL); if(!sz) { ERR("Can't convert path to unixcp! %s\n", wine_dbgstr_w(pathW)); return 0; } pathU = HeapAlloc(GetProcessHeap(), 0, sz + sizeof(append_path)); sz = WideCharToMultiByte(CP_UNIXCP, 0, pathW, -1, pathU, sz, NULL, NULL); if(!sz) { ERR("Can't convert path to unixcp! %s\n", wine_dbgstr_w(pathW)); HeapFree(GetProcessHeap(), 0, pathU); return 0; } strcat(pathU, append_path); TRACE("got openvr runtime path: %s\n", pathU); vrclient_lib = dlopen(pathU, RTLD_NOW); if(!vrclient_lib){ TRACE("unable to load vrclient.so\n"); HeapFree(GetProcessHeap(), 0, pathU); return 0; } vrclient_HmdSystemFactory = dlsym(vrclient_lib, "HmdSystemFactory"); if(!vrclient_HmdSystemFactory){ ERR("unable to load HmdSystemFactory method\n"); HeapFree(GetProcessHeap(), 0, pathU); return 0; } vrclient_VRClientCoreFactory = dlsym(vrclient_lib, "VRClientCoreFactory"); if(!vrclient_VRClientCoreFactory){ ERR("unable to load VRClientCoreFactory method\n"); HeapFree(GetProcessHeap(), 0, pathU); return 0; } HeapFree(GetProcessHeap(), 0, pathU); return 1; } void *CDECL HmdSystemFactory(const char *name, int *return_code) { TRACE("name: %s, return_code: %p\n", name, return_code); if(!load_vrclient()) return NULL; return create_win_interface(name, vrclient_HmdSystemFactory(name, return_code)); } void *CDECL VRClientCoreFactory(const char *name, int *return_code) { TRACE("name: %s, return_code: %p\n", name, return_code); if(!load_vrclient()) return NULL; return create_win_interface(name, vrclient_VRClientCoreFactory(name, return_code)); } static VkDevice_T *(WINAPI *p_get_native_VkDevice)( VkDevice_T * ); static VkInstance_T *(WINAPI *p_get_native_VkInstance)( VkInstance_T * ); static VkPhysicalDevice_T *(WINAPI *p_get_native_VkPhysicalDevice)( VkPhysicalDevice_T * ); static VkPhysicalDevice_T *(WINAPI *p_get_wrapped_VkPhysicalDevice)( VkInstance_T *, VkPhysicalDevice_T * ); static VkQueue_T *(WINAPI *p_get_native_VkQueue)( VkQueue_T * ); static void *get_winevulkan_unix_lib_handle(HMODULE hvulkan) { unixlib_handle_t unix_funcs; NTSTATUS status; Dl_info info; status = NtQueryVirtualMemory(GetCurrentProcess(), hvulkan, (MEMORY_INFORMATION_CLASS)1000 /*MemoryWineUnixFuncs*/, &unix_funcs, sizeof(unix_funcs), NULL); if (status) { WINE_ERR("NtQueryVirtualMemory status %#x.\n", (int)status); return NULL; } if (!dladdr((void *)(ULONG_PTR)unix_funcs, &info)) { WINE_ERR("dladdr failed.\n"); return NULL; } WINE_TRACE("path %s.\n", info.dli_fname); return dlopen(info.dli_fname, RTLD_NOW); } static void load_vk_unwrappers(void) { static HMODULE h = NULL; void *unix_handle; if(h) /* already loaded */ return; h = LoadLibraryA("winevulkan"); if(!h){ ERR("unable to load winevulkan\n"); return; } if (!(unix_handle = get_winevulkan_unix_lib_handle(h))) { ERR("Unable to open winevulkan.so.\n"); return; } #define L( name ) \ if (!(p_##name = dlsym( unix_handle, "__wine_" #name ))) \ { \ ERR( "%s not found.\n", #name ); \ dlclose( unix_handle ); \ return; \ } L(get_native_VkDevice); L(get_native_VkInstance); L(get_native_VkPhysicalDevice); L(get_wrapped_VkPhysicalDevice); L(get_native_VkQueue); #undef L dlclose(unix_handle); } VkDevice_T *get_native_VkDevice( VkDevice_T *device ) { load_vk_unwrappers(); return p_get_native_VkDevice( device ); } VkInstance_T *get_native_VkInstance( VkInstance_T *instance ) { load_vk_unwrappers(); return p_get_native_VkInstance( instance ); } VkPhysicalDevice_T *get_native_VkPhysicalDevice( VkPhysicalDevice_T *device ) { load_vk_unwrappers(); return p_get_native_VkPhysicalDevice( device ); } VkPhysicalDevice_T *get_wrapped_VkPhysicalDevice( VkInstance_T *instance, VkPhysicalDevice_T *device ) { load_vk_unwrappers(); return p_get_wrapped_VkPhysicalDevice( instance, device ); } VkQueue_T *get_native_VkQueue( VkQueue_T *queue ) { load_vk_unwrappers(); return p_get_native_VkQueue( queue ); } static bool is_hmd_present_reg(void) { DWORD type, value, wait_status, size; DWORD is_hmd_present = 0; LSTATUS status; HANDLE event; HKEY vr_key; if ((status = RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Wine\\VR", 0, KEY_READ, &vr_key))) { WINE_ERR("Could not create key, status %#x.\n", status); return FALSE; } size = sizeof(value); if ((status = RegQueryValueExA(vr_key, "state", NULL, &type, (BYTE *)&value, &size))) { WINE_ERR("Could not query value, status %#x.\n", status); RegCloseKey(vr_key); return FALSE; } if (type != REG_DWORD) { WINE_ERR("Unexpected value type %#x.\n", type); RegCloseKey(vr_key); return FALSE; } if (value) { RegCloseKey(vr_key); return value == 1; } event = CreateEventA( NULL, FALSE, FALSE, NULL ); while (1) { if (RegNotifyChangeKeyValue(vr_key, FALSE, REG_NOTIFY_CHANGE_LAST_SET, event, TRUE)) { WINE_ERR("Error registering registry change notification.\n"); goto done; } size = sizeof(value); if ((status = RegQueryValueExA(vr_key, "state", NULL, &type, (BYTE *)&value, &size))) { WINE_ERR("Could not query value, status %#x.\n", status); goto done; } if (value) break; while ((wait_status = WaitForSingleObject(event, 1000)) == WAIT_TIMEOUT) WINE_ERR("VR state wait timeout.\n"); if (wait_status != WAIT_OBJECT_0) { WINE_ERR("Got unexpected wait status %#x.\n", wait_status); break; } } if (value != 1) goto done; size = sizeof(is_hmd_present); if ((status = RegQueryValueExA(vr_key, "is_hmd_present", NULL, &type, (BYTE *)&is_hmd_present, &size))) WINE_ERR("Could not query is_hmd_present value, status %#x.\n", status); done: CloseHandle(event); RegCloseKey(vr_key); return is_hmd_present; } bool ivrclientcore_is_hmd_present(bool (*cpp_func)(void *), void *linux_side, unsigned int version, struct client_core_data *user_data) { TRACE("linux_side %p, compositor_data.client_core_linux_side %p.\n", linux_side, compositor_data.client_core_linux_side); /* BIsHmdPresent() currently always returns FALSE on Linux if called before Init(). * Return true if the value stored by steam.exe helper in registry says the HMD is presnt. */ if (compositor_data.client_core_linux_side || !is_hmd_present_reg()) return cpp_func(linux_side); return TRUE; } EVRInitError ivrclientcore_002_init(EVRInitError (*cpp_func)(void *, EVRApplicationType), void *linux_side, EVRApplicationType application_type, unsigned int version, struct client_core_data *user_data) { EVRInitError error; TRACE("%p, %#x\n", linux_side, application_type); InitializeCriticalSection(&user_data->critical_section); error = cpp_func(linux_side, application_type); if (error) WARN("error %#x\n", error); return error; } EVRInitError ivrclientcore_init(EVRInitError (*cpp_func)(void *, EVRApplicationType, const char *), void *linux_side, EVRApplicationType application_type, const char *startup_info, unsigned int version, struct client_core_data *user_data) { char *startup_info_converted; EVRInitError error; TRACE("%p, %#x, %p\n", linux_side, application_type, startup_info); startup_info_converted = json_convert_startup_info(startup_info); InitializeCriticalSection(&user_data->critical_section); error = cpp_func(linux_side, application_type, startup_info_converted ? startup_info_converted : startup_info); free(startup_info_converted); if (error) WARN("error %#x\n", error); else compositor_data.client_core_linux_side = linux_side; return error; } void *ivrclientcore_get_generic_interface(void *(*cpp_func)(void *, const char *, EVRInitError *), void *linux_side, const char *name_and_version, EVRInitError *error, unsigned int version, struct client_core_data *user_data) { const char *cpp_name_and_version = name_and_version; struct w_steam_iface *win_object; struct generic_interface *iface; pfn_dtor destructor; void *object; TRACE("%p, %p, %p\n", linux_side, name_and_version, error); /* In theory we could pass this along, but we'd have to generate a separate * set of thunks for it. Hopefully this will work as it is. */ if (name_and_version && !strncmp(name_and_version, "FnTable:", 8)) cpp_name_and_version += 8; if (!(object = cpp_func(linux_side, cpp_name_and_version, error))) { WARN("Failed to create %s.\n", name_and_version); return NULL; } if (!(win_object = create_win_interface(name_and_version, object))) { ERR("Failed to create win object %s.\n", name_and_version); return NULL; } if ((destructor = get_win_destructor(name_and_version))) { EnterCriticalSection(&user_data->critical_section); if (array_reserve((void **)&user_data->created_interfaces, &user_data->created_interfaces_size, user_data->created_interface_count + 1, sizeof(*user_data->created_interfaces))) { iface = &user_data->created_interfaces[user_data->created_interface_count++]; iface->object = win_object; iface->dtor = destructor; } else { ERR("Failed to add interface to array.\n"); } LeaveCriticalSection(&user_data->critical_section); } if (name_and_version && !strncmp(name_and_version, "FnTable:", 8)) return *((void **)win_object); return win_object; } static void destroy_compositor_data(void) { TRACE("Destroying compositor data.\n"); memset(&compositor_data, 0, sizeof(compositor_data)); } void ivrclientcore_cleanup(void (*cpp_func)(void *), void *linux_side, unsigned int version, struct client_core_data *user_data) { struct generic_interface *iface; SIZE_T i; TRACE("%p\n", linux_side); EnterCriticalSection(&user_data->critical_section); for (i = 0; i < user_data->created_interface_count; ++i) { iface = &user_data->created_interfaces[i]; iface->dtor(iface->object); } HeapFree(GetProcessHeap(), 0, user_data->created_interfaces); user_data->created_interfaces = NULL; user_data->created_interfaces_size = 0; user_data->created_interface_count = 0; LeaveCriticalSection(&user_data->critical_section); DeleteCriticalSection(&user_data->critical_section); cpp_func(linux_side); destroy_compositor_data(); } struct submit_data { void *linux_side; EVRCompositorError (*submit)(void *, EVREye, Texture_t *, VRTextureBounds_t *, EVRSubmitFlags); EVREye eye; Texture_t texture; VRTextureWithPose_t texture_pose; VRTextureWithDepth_t texture_depth; VRTextureWithPoseAndDepth_t texture_both; VRTextureBounds_t bounds; EVRSubmitFlags flags; }; void ivrcompositor_005_submit( void (*cpp_func)(void *, Hmd_Eye, void *, Compositor_TextureBounds *), void *linux_side, Hmd_Eye eye, const void *texture, Compositor_TextureBounds *bounds, unsigned int version) { TRACE("%p, %#x, %p, %p\n", linux_side, eye, texture, bounds); return cpp_func(linux_side, eye, texture, bounds); } VRCompositorError ivrcompositor_006_submit( VRCompositorError (*cpp_func)(void *, Hmd_Eye, void *, VRTextureBounds_t *), void *linux_side, Hmd_Eye eye, const void *texture, const VRTextureBounds_t *bounds, unsigned int version) { TRACE("%p, %#x, %p, %p\n", linux_side, eye, texture, bounds); return cpp_func(linux_side, eye, texture, bounds); } VRCompositorError ivrcompositor_007_submit( VRCompositorError (*cpp_func)(void *, Hmd_Eye, GraphicsAPIConvention, void *, const VRTextureBounds_t *), void *linux_side, Hmd_Eye eye, GraphicsAPIConvention api, const void *texture, const VRTextureBounds_t *bounds, unsigned int version) { TRACE("%p, %#x, %#x, %p, %p\n", linux_side, eye, api, texture, bounds); if (api == API_DirectX) FIXME("Not implemented Direct3D API!\n"); return cpp_func(linux_side, eye, api, texture, bounds); } VRCompositorError ivrcompositor_008_submit( VRCompositorError (*cpp_func)(void *, Hmd_Eye, GraphicsAPIConvention, void *, const VRTextureBounds_t *, VRSubmitFlags_t), void *linux_side, Hmd_Eye eye, GraphicsAPIConvention api, const void *texture, const VRTextureBounds_t *bounds, VRSubmitFlags_t flags, unsigned int version) { TRACE("%p, %#x, %#x, %p, %p, %#x\n", linux_side, eye, api, texture, bounds, flags); if (api == API_DirectX) FIXME("Not implemented Direct3D API!\n"); return cpp_func(linux_side, eye, api, texture, bounds, flags); } Texture_t vrclient_translate_texture_dxvk( const Texture_t *texture, struct VRVulkanTextureData_t *vkdata, IDXGIVkInteropSurface *dxvk_surface, IDXGIVkInteropDevice **p_dxvk_device, VkImageLayout *image_layout, VkImageCreateInfo *image_info ) { struct Texture_t vktexture; VkImage image_handle; dxvk_surface->lpVtbl->GetDevice(dxvk_surface, p_dxvk_device); (*p_dxvk_device)->lpVtbl->GetVulkanHandles(*p_dxvk_device, &vkdata->m_pInstance, &vkdata->m_pPhysicalDevice, &vkdata->m_pDevice); (*p_dxvk_device)->lpVtbl->GetSubmissionQueue(*p_dxvk_device, &vkdata->m_pQueue, &vkdata->m_nQueueFamilyIndex); image_info->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_info->pNext = NULL; dxvk_surface->lpVtbl->GetVulkanImageInfo(dxvk_surface, &image_handle, image_layout, image_info); vkdata->m_nImage = (uint64_t)image_handle; vkdata->m_pDevice = get_native_VkDevice(vkdata->m_pDevice); vkdata->m_pPhysicalDevice = get_native_VkPhysicalDevice(vkdata->m_pPhysicalDevice); vkdata->m_pInstance = get_native_VkInstance(vkdata->m_pInstance); vkdata->m_pQueue = get_native_VkQueue(vkdata->m_pQueue); vkdata->m_nWidth = image_info->extent.width; vkdata->m_nHeight = image_info->extent.height; vkdata->m_nFormat = image_info->format; vkdata->m_nSampleCount = image_info->samples; vktexture = *texture; vktexture.handle = vkdata; vktexture.eType = TextureType_Vulkan; return vktexture; } static EVRCompositorError ivrcompositor_submit_dxvk( EVRCompositorError (*cpp_func)(void *, EVREye, const Texture_t *, const VRTextureBounds_t *, EVRSubmitFlags), void *linux_side, EVREye eye, const Texture_t *texture, const VRTextureBounds_t *bounds, EVRSubmitFlags flags, unsigned int version, IDXGIVkInteropSurface *dxvk_surface) { static const EVRSubmitFlags supported_flags = Submit_LensDistortionAlreadyApplied | Submit_FrameDiscontinuty; struct VRVulkanTextureArrayData_t vkdata; IDXGIVkInteropDevice *dxvk_device; struct Texture_t vktexture; VkImageLayout image_layout; VkImageCreateInfo image_info; VkImageSubresourceRange subresources; EVRCompositorError err; vktexture = vrclient_translate_texture_dxvk(texture, &vkdata.t, dxvk_surface, &dxvk_device, &image_layout, &image_info); compositor_data.dxvk_device = dxvk_device; if (flags & ~supported_flags) FIXME("Unhandled flags %#x.\n", flags); if (image_info.arrayLayers > 1) { vkdata.m_unArrayIndex = eye; vkdata.m_unArraySize = image_info.arrayLayers; flags |= Submit_VulkanTextureWithArrayData; } subresources.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresources.baseMipLevel = 0; subresources.levelCount = image_info.mipLevels; subresources.baseArrayLayer = 0; subresources.layerCount = image_info.arrayLayers; dxvk_device->lpVtbl->TransitionSurfaceLayout(dxvk_device, dxvk_surface, &subresources, image_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); dxvk_device->lpVtbl->FlushRenderingCommands(dxvk_device); dxvk_device->lpVtbl->LockSubmissionQueue(dxvk_device); err = cpp_func(linux_side, eye, &vktexture, bounds, flags); dxvk_device->lpVtbl->ReleaseSubmissionQueue(dxvk_device); dxvk_device->lpVtbl->TransitionSurfaceLayout(dxvk_device, dxvk_surface, &subresources, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image_layout); dxvk_device->lpVtbl->Release(dxvk_device); dxvk_surface->lpVtbl->Release(dxvk_surface); return err; } static EVRCompositorError ivrcompositor_submit_vulkan( EVRCompositorError (*cpp_func)(void *, EVREye, const Texture_t *, const VRTextureBounds_t *, EVRSubmitFlags), void *linux_side, EVREye eye, const Texture_t *texture, const VRTextureBounds_t *bounds, EVRSubmitFlags flags, unsigned int version) { struct VRVulkanTextureData_t our_depth_vkdata, *their_vkdata; struct VRVulkanTextureArrayData_t our_vkdata; VRTextureWithPoseAndDepth_t our_both; VRTextureWithDepth_t our_depth; VRTextureWithPose_t our_pose; Texture_t our_texture; const void *tex = texture; their_vkdata = texture->handle; memcpy(&our_vkdata, their_vkdata, flags & Submit_VulkanTextureWithArrayData ? sizeof(struct VRVulkanTextureArrayData_t) : sizeof(struct VRVulkanTextureData_t)); our_vkdata.t.m_pDevice = get_native_VkDevice(our_vkdata.t.m_pDevice); our_vkdata.t.m_pPhysicalDevice = get_native_VkPhysicalDevice(our_vkdata.t.m_pPhysicalDevice); our_vkdata.t.m_pInstance = get_native_VkInstance(our_vkdata.t.m_pInstance); our_vkdata.t.m_pQueue = get_native_VkQueue(our_vkdata.t.m_pQueue); switch (flags & (Submit_TextureWithPose | Submit_TextureWithDepth)) { case 0: our_texture = *texture; our_texture.handle = &our_vkdata; tex = &our_texture; break; case Submit_TextureWithPose: our_pose = *(VRTextureWithPose_t *)texture; our_pose.texture.handle = &our_vkdata; tex = &our_pose; break; case Submit_TextureWithDepth: our_depth = *(VRTextureWithDepth_t *)texture; our_depth.texture.handle = &our_vkdata; tex = &our_depth; break; case Submit_TextureWithPose | Submit_TextureWithDepth: our_both = *(VRTextureWithPoseAndDepth_t *)texture; our_both.texture.handle = &our_vkdata; their_vkdata = our_both.depth.handle; our_depth_vkdata = *their_vkdata; our_depth_vkdata.m_pDevice = get_native_VkDevice(our_depth_vkdata.m_pDevice); our_depth_vkdata.m_pPhysicalDevice = get_native_VkPhysicalDevice(our_depth_vkdata.m_pPhysicalDevice); our_depth_vkdata.m_pInstance = get_native_VkInstance(our_depth_vkdata.m_pInstance); our_depth_vkdata.m_pQueue = get_native_VkQueue(our_depth_vkdata.m_pQueue); our_both.depth.handle = &our_depth_vkdata; tex = &our_both; break; } return cpp_func(linux_side, eye, tex, bounds, flags); } EVRCompositorError ivrcompositor_submit( EVRCompositorError (*cpp_func)(void *, EVREye, const Texture_t *, const VRTextureBounds_t *, EVRSubmitFlags), void *linux_side, EVREye eye, const Texture_t *texture, const VRTextureBounds_t *bounds, EVRSubmitFlags flags, unsigned int version) { IDXGIVkInteropSurface *dxvk_surface; IUnknown *texture_iface; HRESULT hr; TRACE("%p, %#x, %p, %p, %#x\n", linux_side, eye, texture, bounds, flags); compositor_data.handoff_called = FALSE; switch (texture->eType) { case TextureType_DirectX: { TRACE("D3D11\n"); if (!texture->handle) { WARN("No D3D11 texture %p.\n", texture); return cpp_func(linux_side, eye, texture, bounds, flags); } texture_iface = texture->handle; if (SUCCEEDED(hr = texture_iface->lpVtbl->QueryInterface(texture_iface, &IID_IDXGIVkInteropSurface, (void **)&dxvk_surface))) { return ivrcompositor_submit_dxvk(cpp_func, linux_side, eye, texture, bounds, flags, version, dxvk_surface); } WARN("Invalid D3D11 texture %p.\n", texture); return cpp_func(linux_side, eye, texture, bounds, flags); } case TextureType_Vulkan: return ivrcompositor_submit_vulkan(cpp_func, linux_side, eye, texture, bounds, flags, version); default: return cpp_func(linux_side, eye, texture, bounds, flags); } } void ivrcompositor_008_set_skybox_override( void (*cpp_func)(void *, GraphicsAPIConvention, void *, void *, void *, void *, void *, void *), void *linux_side, GraphicsAPIConvention api, void *front, void *back, void *left, void *right, void *top, void *bottom, unsigned int version) { TRACE("%p, %#x, %p, %p, %p, %p, %p, %p.\n", linux_side, api, front, back, left, right, top, bottom); if (api == API_DirectX) FIXME("Not implemented Direct3D API.\n"); cpp_func(linux_side, api, front, back, left, right, top, bottom); } static EVRCompositorError ivrcompositor_set_skybox_override_d3d11( EVRCompositorError (*cpp_func)(void *, const Texture_t *textures, uint32_t count), void *linux_side, const Texture_t *textures, uint32_t count) { struct VRVulkanTextureData_t vkdata[6]; IDXGIVkInteropSurface *dxvk_surface; struct Texture_t vktexture[6]; EVRCompositorError result; unsigned int i; for (i = 0; i < count; ++i) { const Texture_t *texture = &textures[i]; IUnknown *texture_iface; if (!texture->handle) { ERR("No D3D11 texture %p.\n", texture); return cpp_func(linux_side, textures, count); } if (textures[i].eType != TextureType_DirectX) { FIXME("Mixing texture types is not supported.\n"); return 0; } texture_iface = texture->handle; if (SUCCEEDED(texture_iface->lpVtbl->QueryInterface(texture_iface, &IID_IDXGIVkInteropSurface, (void **)&dxvk_surface))) { VkImageSubresourceRange subresources; IDXGIVkInteropDevice *dxvk_device; VkImageCreateInfo image_info; VkImageLayout image_layout; vktexture[i] = vrclient_translate_texture_dxvk(texture, &vkdata[i], dxvk_surface, &dxvk_device, &image_layout, &image_info); if (compositor_data.dxvk_device && dxvk_device != compositor_data.dxvk_device) { ERR("Invalid dxvk_device %p, previous %p.\n", dxvk_device, compositor_data.dxvk_device); dxvk_surface->lpVtbl->Release(dxvk_surface); dxvk_device->lpVtbl->Release(dxvk_device); return 0; } compositor_data.dxvk_device = dxvk_device; subresources.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresources.baseMipLevel = 0; subresources.levelCount = image_info.mipLevels; subresources.baseArrayLayer = 0; subresources.layerCount = image_info.arrayLayers; dxvk_device->lpVtbl->TransitionSurfaceLayout(dxvk_device, dxvk_surface, &subresources, image_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); dxvk_surface->lpVtbl->Release(dxvk_surface); dxvk_device->lpVtbl->Release(dxvk_device); continue; } FIXME("Unsupported d3d11 texture %p, i %u.\n", texture, i); return 0; } compositor_data.dxvk_device->lpVtbl->FlushRenderingCommands(compositor_data.dxvk_device); compositor_data.dxvk_device->lpVtbl->LockSubmissionQueue(compositor_data.dxvk_device); result = cpp_func(linux_side, vktexture, count); compositor_data.dxvk_device->lpVtbl->ReleaseSubmissionQueue(compositor_data.dxvk_device); TRACE("result %u.\n", result); return result; } EVRCompositorError ivrcompositor_set_skybox_override( EVRCompositorError (*cpp_func)(void *, const Texture_t *textures, uint32_t count), void *linux_side, const Texture_t *textures, uint32_t count, unsigned int version) { TRACE("cpp_func %p, linux_side %p, textures %p, count %u, version %u.\n", cpp_func, linux_side, textures, count, version); if (!count || count > 6) { WARN("Invalid texture count %u.\n", count); return cpp_func(linux_side, textures, count); } if (textures[0].eType == TextureType_DirectX) return ivrcompositor_set_skybox_override_d3d11(cpp_func, linux_side, textures, count); FIXME("Conversion for type %u is not supported.\n", textures[0].eType); return 0; } struct post_present_handoff_data { void *linux_side; void (*post_present_handoff)(void *linux_side); }; void ivrcompositor_post_present_handoff(void (*cpp_func)(void *), void *linux_side, unsigned int version) { TRACE("%p\n", linux_side); if (compositor_data.dxvk_device) { compositor_data.dxvk_device->lpVtbl->LockSubmissionQueue(compositor_data.dxvk_device); if (!compositor_data.d3d11_explicit_handoff && version >= 21) { /* PostPresentHandoff can be used with d3d11 without SetExplicitTimingMode * (which is Vulkan / d3d12 only), but doing the same with Vulkan results * in lockups and crashes. */ cppIVRCompositor_IVRCompositor_021_SetExplicitTimingMode(linux_side, VRCompositorTimingMode_Explicit_ApplicationPerformsPostPresentHandoff); compositor_data.d3d11_explicit_handoff = TRUE; } } cpp_func(linux_side); compositor_data.handoff_called = TRUE; if (compositor_data.dxvk_device) compositor_data.dxvk_device->lpVtbl->ReleaseSubmissionQueue(compositor_data.dxvk_device); } struct explicit_timing_data { void *linux_side; unsigned int version; }; EVRCompositorError ivrcompositor_wait_get_poses( EVRCompositorError (cpp_func)(void *, TrackedDevicePose_t *, uint32_t, TrackedDevicePose_t *, uint32_t), void *linux_side, TrackedDevicePose_t *render_poses, uint32_t render_pose_count, TrackedDevicePose_t *game_poses, uint32_t game_pose_count, unsigned int version) { EVRCompositorError r; TRACE("%p, %p, %u, %p, %u\n", linux_side, render_poses, render_pose_count, game_poses, game_pose_count); if (compositor_data.dxvk_device) { compositor_data.dxvk_device->lpVtbl->LockSubmissionQueue(compositor_data.dxvk_device); if (compositor_data.d3d11_explicit_handoff && !compositor_data.handoff_called) { /* Calling handoff after submit is optional for d3d11 but mandatory for Vulkan * if explicit timing mode is set. */ cppIVRCompositor_IVRCompositor_022_PostPresentHandoff(linux_side); } } r = cpp_func(linux_side, render_poses, render_pose_count, game_poses, game_pose_count); if (compositor_data.dxvk_device) { if (compositor_data.d3d11_explicit_handoff) cppIVRCompositor_IVRCompositor_022_SubmitExplicitTimingData(linux_side); compositor_data.dxvk_device->lpVtbl->ReleaseSubmissionQueue(compositor_data.dxvk_device); } return r; } uint32_t ivrcompositor_get_vulkan_device_extensions_required( uint32_t (*cpp_func)(void *, VkPhysicalDevice_T *, char *, uint32_t), void *linux_side, VkPhysicalDevice_T *phys_dev, char *value, uint32_t bufsize, unsigned int version) { uint32_t ret; phys_dev = get_native_VkPhysicalDevice(phys_dev); ret = cpp_func(linux_side, phys_dev, value, bufsize); TRACE("ret %u, value %s.\n", ret, value); return ret; } vrmb_typeb ivrmailbox_undoc3( vrmb_typeb (*cpp_func)(void *, vrmb_typea, const char *, const char *), void *linux_side, vrmb_typea a, const char *b, const char *c, unsigned int version) { vrmb_typeb r; char *converted = json_convert_paths(c); r = cpp_func(linux_side, a, b, converted ? converted : c); free(converted); return r; }