#include #include #include #include #include #include #include "windef.h" #include "winbase.h" #include "winnls.h" #include "wine/debug.h" #include "wine/library.h" #include "vrclient_defs.h" #include "vrclient_private.h" #include "initguid.h" #if !defined(__APPLE__) || defined(__x86_64__) /* 32-bit Mac doesn't support Vulkan and thus DXVK */ #include "dxvk-interop.h" #endif #include "wined3d-interop.h" #include "cxx.h" #include "flatapi.h" #include "cppIVRClientCore_IVRClientCore_003.h" #include "cppIVRCompositor_IVRCompositor_021.h" #include "cppIVRCompositor_IVRCompositor_022.h" WINE_DEFAULT_DEBUG_CHANNEL(vrclient); 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; } return TRUE; } #if 0 uint32 vrclient_unix_path_to_dos_path(uint32 api_result, char *inout, uint32 inout_bytes) { WCHAR *converted; uint32 r; if(api_result == 0) return 0; converted = wine_get_dos_file_name(inout); if(!converted){ WARN("Unable to convert unix filename to DOS: %s\n", inout); *inout = 0; return 0; } r = WideCharToMultiByte(CP_ACP, 0, converted, -1, inout, inout_bytes, NULL, NULL); HeapFree(GetProcessHeap(), 0, converted); if(r > 0) return r - 1; return 0; } #endif 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)(void *); static const struct { const char *iface_version; void *(*ctor)(void *); void (*dtor)(void *); } constructors[] = { #include "win_constructors_table.dat" }; void *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_lib; static void *(*vrclient_VRClientCoreFactory)(const char *name, int *return_code); static int load_vrclient(void) { char path[PATH_MAX]; if(vrclient_lib) return 1; /* PROTON_VR_RUNTIME is provided by the proton setup script */ if(!getenv("PROTON_VR_RUNTIME")){ TRACE("Linux OpenVR runtime is not available\n"); return 0; } #ifdef _WIN64 snprintf(path, PATH_MAX, "%s/bin/linux64/vrclient.so", getenv("PROTON_VR_RUNTIME")); #else snprintf(path, PATH_MAX, "%s/bin/vrclient.so", getenv("PROTON_VR_RUNTIME")); #endif TRACE("got openvr runtime path: %s\n", path); vrclient_lib = wine_dlopen(path, RTLD_NOW, NULL, 0); if(!vrclient_lib){ ERR("unable to load vrclient.so\n"); return 0; } vrclient_VRClientCoreFactory = wine_dlsym(vrclient_lib, "VRClientCoreFactory", NULL, 0); if(!vrclient_VRClientCoreFactory){ ERR("unable to load VRClientCoreFactory method\n"); return 0; } return 1; } 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 *get_native_VkDevice)(VkDevice_T *); static VkInstance_T *(WINAPI *get_native_VkInstance)(VkInstance_T *); static VkPhysicalDevice_T *(WINAPI *get_native_VkPhysicalDevice)(VkPhysicalDevice_T *); static VkPhysicalDevice_T *(WINAPI *get_wrapped_VkPhysicalDevice)(VkInstance_T *, VkPhysicalDevice_T *); static VkQueue_T *(WINAPI *get_native_VkQueue)(VkQueue_T *); static void load_vk_unwrappers(void) { static HMODULE h = NULL; if(h) /* already loaded */ return; h = LoadLibraryA("winevulkan"); if(!h){ ERR("unable to load winevulkan\n"); return; } get_native_VkDevice = (void*)GetProcAddress(h, "__wine_get_native_VkDevice"); get_native_VkInstance = (void*)GetProcAddress(h, "__wine_get_native_VkInstance"); get_native_VkPhysicalDevice = (void*)GetProcAddress(h, "__wine_get_native_VkPhysicalDevice"); get_wrapped_VkPhysicalDevice = (void*)GetProcAddress(h, "__wine_get_wrapped_VkPhysicalDevice"); get_native_VkQueue = (void*)GetProcAddress(h, "__wine_get_native_VkQueue"); } EVRInitError ivrclientcore_002_init(EVRInitError (*cpp_func)(void *, EVRApplicationType), void *linux_side, EVRApplicationType application_type, unsigned int version, struct client_core_data *user_data) { TRACE("%p, %#x\n", linux_side, application_type); InitializeCriticalSection(&user_data->critical_section); return cpp_func(linux_side, application_type); } 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) { TRACE("%p, %#x, %p\n", linux_side, application_type, startup_info); InitializeCriticalSection(&user_data->critical_section); return cpp_func(linux_side, application_type, startup_info); } 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 generic_interface *iface; pfn_dtor destructor; void *win_object; 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; } 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); } void get_dxgi_output_info(void *cpp_func, void *linux_side, int32_t *adapter_idx, unsigned int version) { TRACE("%p\n", adapter_idx); *adapter_idx = 0; } void get_dxgi_output_info2(void *cpp_func, void *linux_side, int32_t *adapter_idx, int32_t *output_idx, unsigned int version) { TRACE("%p, %p\n", adapter_idx, output_idx); *adapter_idx = 0; *output_idx = 0; } void ivrsystem_016_get_output_device( void (*cpp_func)(void *, uint64_t *, ETextureType), void *linux_side, uint64_t *out_device, ETextureType type, unsigned int version) { cpp_func(linux_side, out_device, type); } void ivrsystem_get_output_device( void (*cpp_func)(void *, uint64_t *, ETextureType, VkInstance_T *), void *linux_side, uint64_t *out_device, ETextureType type, VkInstance_T *wrapped_instance, unsigned int version) { switch(type){ case TextureType_Vulkan: { VkInstance_T *native_instance; load_vk_unwrappers(); native_instance = get_native_VkInstance(wrapped_instance); cpp_func(linux_side, out_device, type, native_instance); *out_device = (uint64_t)(intptr_t)get_wrapped_VkPhysicalDevice(wrapped_instance, (VkPhysicalDevice_T *)(intptr_t)*out_device); return; } default: cpp_func(linux_side, out_device, type, wrapped_instance); return; } } 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; }; static CDECL void d3d11_texture_callback(unsigned int gl_texture, unsigned int gl_depth_texture, const void *data, unsigned int data_size) { const struct submit_data *submit_data = data; VRCompositorError error = 0; VRTextureBounds_t bounds; Texture_t texture, *tex; VRTextureWithPose_t texture_pose; VRTextureWithDepth_t texture_depth; VRTextureWithPoseAndDepth_t texture_both; TRACE("texture %u, data {%p, %u}\n", gl_texture, data, data_size); switch(submit_data->flags & (Submit_TextureWithPose | Submit_TextureWithDepth)){ case 0: texture = submit_data->texture; texture.handle = (void *)(UINT_PTR)gl_texture; texture.eType = TextureType_OpenGL; tex = &texture; break; case Submit_TextureWithPose: texture_pose = submit_data->texture_pose; texture_pose.texture.handle = (void *)(UINT_PTR)gl_texture; texture_pose.texture.eType = TextureType_OpenGL; tex = (Texture_t *)&texture_pose; break; case Submit_TextureWithDepth: texture_depth = submit_data->texture_depth; texture_depth.texture.handle = (void *)(UINT_PTR)gl_texture; texture_depth.texture.eType = TextureType_OpenGL; texture_depth.depth.handle = (void *)(UINT_PTR)gl_depth_texture; tex = (Texture_t *)&texture_depth; break; case Submit_TextureWithPose | Submit_TextureWithDepth: texture_both = submit_data->texture_both; texture_both.texture.handle = (void *)(UINT_PTR)gl_texture; texture_both.texture.eType = TextureType_OpenGL; texture_both.depth.handle = (void *)(UINT_PTR)gl_depth_texture; tex = (Texture_t *)&texture_both; break; } /* Textures are upside-down in wined3d. */ bounds = submit_data->bounds; bounds.vMin = submit_data->bounds.vMax; bounds.vMax = submit_data->bounds.vMin; error = submit_data->submit(submit_data->linux_side, submit_data->eye, tex, &bounds, submit_data->flags); if (error) ERR("error %#x\n", error); } void ivrcompositor_005_submit( void (*cpp_func)(void *, Hmd_Eye, void *, Compositor_TextureBounds *), void *linux_side, Hmd_Eye eye, void *texture, Compositor_TextureBounds *bounds, unsigned int version, struct compositor_data *user_data) { 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, void *texture, VRTextureBounds_t *bounds, unsigned int version, struct compositor_data *user_data) { 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 *, VRTextureBounds_t *), void *linux_side, Hmd_Eye eye, GraphicsAPIConvention api, void *texture, VRTextureBounds_t *bounds, unsigned int version, struct compositor_data *user_data) { 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 *, VRTextureBounds_t *, VRSubmitFlags_t), void *linux_side, Hmd_Eye eye, GraphicsAPIConvention api, void *texture, VRTextureBounds_t *bounds, VRSubmitFlags_t flags, unsigned int version, struct compositor_data *user_data) { 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); } static void *our_compositor; static void get_our_compositor(void) { void *client_core = vrclient_VRClientCoreFactory("IVRClientCore_003", NULL); our_compositor = cppIVRClientCore_IVRClientCore_003_GetGenericInterface(client_core, "IVRCompositor_022", NULL); } EVRCompositorError ivrcompositor_submit( EVRCompositorError (*cpp_func)(void *, EVREye, Texture_t *, VRTextureBounds_t *, EVRSubmitFlags), void *linux_side, EVREye eye, Texture_t *texture, VRTextureBounds_t *bounds, EVRSubmitFlags flags, unsigned int version, struct compositor_data *user_data) { IWineD3D11Texture2D *wine_texture; IWineD3D11Device *wined3d_device; struct submit_data submit_data; IUnknown *texture_iface, *depth_texture = NULL; ID3D11Device *device; HRESULT hr; #if !defined(__APPLE__) || defined(__x86_64__) IDXGIVkInteropSurface *dxvk_surface; IDXGIVkInteropDevice *dxvk_device; #endif TRACE("%p, %#x, %p, %p, %#x\n", linux_side, eye, texture, bounds, flags); switch (texture->eType) { case TextureType_DirectX: { TRACE("D3D11\n"); texture_iface = texture->handle; hr = texture_iface->lpVtbl->QueryInterface(texture_iface, &IID_IWineD3D11Texture2D, (void **)&wine_texture); if (SUCCEEDED(hr)) { wine_texture->lpVtbl->GetDevice(wine_texture, &device); if (user_data->d3d11_device != device) { void *timing_compositor = linux_side; if (user_data->d3d11_device) FIXME("Previous submit was from different D3D11 device.\n"); user_data->d3d11_device = device; if (SUCCEEDED(hr = device->lpVtbl->QueryInterface(device, &IID_IWineD3D11Device, (void **)&wined3d_device))) { user_data->wined3d_device = wined3d_device; wined3d_device->lpVtbl->Release(wined3d_device); } else { ERR("Failed to get device, hr %#x.\n", hr); user_data->wined3d_device = NULL; } TRACE("Enabling explicit timing mode.\n"); switch (version) { /* older, supported versions */ case 21: cppIVRCompositor_IVRCompositor_021_SetExplicitTimingMode(linux_side, VRCompositorTimingMode_Explicit_ApplicationPerformsPostPresentHandoff); break; default: { if(!our_compositor){ TRACE("Performing hack for old compositor version %u.\n", version); get_our_compositor(); } timing_compositor = our_compositor; } /* fall through, below version MUST match version in get_our_compositor() */ case 22: cppIVRCompositor_IVRCompositor_022_SetExplicitTimingMode(timing_compositor, VRCompositorTimingMode_Explicit_ApplicationPerformsPostPresentHandoff); break; } } device->lpVtbl->Release(device); submit_data.linux_side = linux_side; submit_data.submit = cpp_func; submit_data.eye = eye; switch(flags & (Submit_TextureWithPose | Submit_TextureWithDepth)){ case 0: submit_data.texture = *texture; break; case Submit_TextureWithPose: submit_data.texture_pose = *(VRTextureWithPose_t *)texture; break; case Submit_TextureWithDepth: submit_data.texture_depth = *(VRTextureWithDepth_t *)texture; depth_texture = (IUnknown*)submit_data.texture_depth.depth.handle; break; case Submit_TextureWithPose | Submit_TextureWithDepth: submit_data.texture_both = *(VRTextureWithPoseAndDepth_t *)texture; depth_texture = (IUnknown*)submit_data.texture_both.depth.handle; break; } submit_data.bounds = *bounds; submit_data.flags = flags; wine_texture->lpVtbl->access_gl_texture(wine_texture, d3d11_texture_callback, depth_texture, &submit_data, sizeof(submit_data)); wine_texture->lpVtbl->Release(wine_texture); return 0; } #if !defined(__APPLE__) || defined(__x86_64__) hr = texture_iface->lpVtbl->QueryInterface(texture_iface, &IID_IDXGIVkInteropSurface, (void **)&dxvk_surface); if (SUCCEEDED(hr)) { struct VRVulkanTextureData_t vkdata; struct Texture_t vktexture; VkImage image_handle; VkImageLayout image_layout; VkImageCreateInfo image_info; VkImageSubresourceRange subresources; EVRCompositorError err; dxvk_surface->lpVtbl->GetDevice( dxvk_surface, &dxvk_device); user_data->dxvk_device = dxvk_device; dxvk_device->lpVtbl->GetVulkanHandles( dxvk_device, &vkdata.m_pInstance, &vkdata.m_pPhysicalDevice, &vkdata.m_pDevice); dxvk_device->lpVtbl->GetSubmissionQueue( dxvk_device, &vkdata.m_pQueue, &vkdata.m_nQueueFamilyIndex); // DXVK needs this to be initialized correctly 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); load_vk_unwrappers(); 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; 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; } #endif ERR("Invalid D3D11 texture %p.\n", texture); return cpp_func(linux_side, eye, texture, bounds, flags); } case TextureType_Vulkan: { struct VRVulkanTextureData_t our_vkdata, our_depth_vkdata, *their_vkdata; Texture_t our_texture, *tex; VRTextureWithPose_t our_pose; VRTextureWithDepth_t our_depth; VRTextureWithPoseAndDepth_t our_both; load_vk_unwrappers(); their_vkdata = (struct VRVulkanTextureData_t*)texture->handle; our_vkdata = *their_vkdata; our_vkdata.m_pDevice = get_native_VkDevice(our_vkdata.m_pDevice); our_vkdata.m_pPhysicalDevice = get_native_VkPhysicalDevice(our_vkdata.m_pPhysicalDevice); our_vkdata.m_pInstance = get_native_VkInstance(our_vkdata.m_pInstance); our_vkdata.m_pQueue = get_native_VkQueue(our_vkdata.m_pQueue); switch(flags & (Submit_TextureWithPose | Submit_TextureWithDepth)){ case 0: our_texture = *texture; our_texture.handle = &our_vkdata; tex = (Texture_t *)&our_texture; break; case Submit_TextureWithPose: our_pose = *(VRTextureWithPose_t *)texture; our_pose.texture.handle = &our_vkdata; tex = (Texture_t *)&our_pose; break; case Submit_TextureWithDepth: our_depth = *(VRTextureWithDepth_t *)texture; our_depth.texture.handle = &our_vkdata; their_vkdata = (struct VRVulkanTextureData_t *)our_depth.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_depth.depth.handle = &our_depth_vkdata; tex = (Texture_t *)&our_depth; break; case Submit_TextureWithPose | Submit_TextureWithDepth: our_both = *(VRTextureWithPoseAndDepth_t *)texture; our_both.texture.handle = &our_vkdata; their_vkdata = (struct VRVulkanTextureData_t *)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 = (Texture_t *)&our_both; break; } return cpp_func(linux_side, eye, tex, bounds, flags); } default: return cpp_func(linux_side, eye, texture, bounds, flags); } } struct post_present_handoff_data { void *linux_side; void (*post_present_handoff)(void *linux_side); }; static CDECL void d3d11_post_present_handoff_callback(const void *data, unsigned int data_size) { const struct post_present_handoff_data *callback_data = data; TRACE("data {%p, %u}\n", data, data_size); callback_data->post_present_handoff(callback_data->linux_side); } void ivrcompositor_post_present_handoff(void (*cpp_func)(void *), void *linux_side, unsigned int version, struct compositor_data *user_data) { struct post_present_handoff_data data; IWineD3D11Device *wined3d_device; TRACE("%p\n", linux_side); if ((wined3d_device = user_data->wined3d_device)) { TRACE("wined3d device %p\n", wined3d_device); data.linux_side = linux_side; data.post_present_handoff = cpp_func; wined3d_device->lpVtbl->run_on_command_stream(wined3d_device, d3d11_post_present_handoff_callback, &data, sizeof(data)); return; } #if !defined(__APPLE__) || defined(__x86_64__) if (user_data->dxvk_device) user_data->dxvk_device->lpVtbl->LockSubmissionQueue(user_data->dxvk_device); #endif cpp_func(linux_side); #if !defined(__APPLE__) || defined(__x86_64__) if (user_data->dxvk_device) user_data->dxvk_device->lpVtbl->ReleaseSubmissionQueue(user_data->dxvk_device); #endif } struct explicit_timing_data { void *linux_side; unsigned int version; }; static CDECL void d3d11_explicit_timing_callback(const void *data, unsigned int data_size) { const struct explicit_timing_data *callback_data = data; EVRCompositorError error; void *timing_compositor = callback_data->linux_side; TRACE("data {%p, %u}\n", data, data_size); switch (callback_data->version) { case 21: error = cppIVRCompositor_IVRCompositor_021_SubmitExplicitTimingData(callback_data->linux_side); break; default: { if(!our_compositor){ TRACE("Performing hack for old compositor version %u.\n", callback_data->version); get_our_compositor(); } timing_compositor = our_compositor; } /* fall through, below version MUST match version in get_our_compositor() */ case 22: error = cppIVRCompositor_IVRCompositor_022_SubmitExplicitTimingData(timing_compositor); break; } if (error) ERR("error %#x\n", error); } 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, struct compositor_data *user_data) { struct explicit_timing_data data; IWineD3D11Device *wined3d_device; EVRCompositorError r; TRACE("%p, %p, %u, %p, %u\n", linux_side, render_poses, render_pose_count, game_poses, game_pose_count); #if !defined(__APPLE__) || defined(__x86_64__) if (user_data->dxvk_device) user_data->dxvk_device->lpVtbl->LockSubmissionQueue(user_data->dxvk_device); #endif r = cpp_func(linux_side, render_poses, render_pose_count, game_poses, game_pose_count); #if !defined(__APPLE__) || defined(__x86_64__) if (user_data->dxvk_device) user_data->dxvk_device->lpVtbl->ReleaseSubmissionQueue(user_data->dxvk_device); #endif if ((wined3d_device = user_data->wined3d_device)) { TRACE("wined3d device %p\n", wined3d_device); /* We need to call IVRCompositor::SubmitExplicitTimingData() before the * first flush of the frame. * * Sending IVRCompositor::SubmitExplicitTimingData() to the command * stream immediately after IVRCompositor::WaitGetPoses() seems * reasonable. */ data.linux_side = linux_side; data.version = version; wined3d_device->lpVtbl->run_on_command_stream(wined3d_device, d3d11_explicit_timing_callback, &data, sizeof(data)); } 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, struct compositor_data *user_data) { load_vk_unwrappers(); phys_dev = get_native_VkPhysicalDevice(phys_dev); return cpp_func(linux_side, phys_dev, value, bufsize); } void destroy_compositor_data(struct compositor_data *data) { IWineD3D11Device *wined3d_device; TRACE("%p\n", data); if ((wined3d_device = data->wined3d_device)) { TRACE("Waiting for device %p\n", wined3d_device); wined3d_device->lpVtbl->wait_idle(wined3d_device); } } /* call_flat_method() definition */ #ifdef __i386__ __ASM_GLOBAL_FUNC(call_flat_method, "popl %eax\n\t" "pushl %ecx\n\t" "pushl %eax\n\t" "jmp *%edx"); #else // handles "this" and up to 3 parameters __ASM_GLOBAL_FUNC(call_flat_method3, "movq %r8, %r9\n\t" // shift over arguments "movq %rdx, %r8\n\t" "movq %rcx, %rdx\n\t" "movq %r10, %rcx\n\t" // add This pointer "jmp *%r11"); __ASM_GLOBAL_FUNC(call_flat_method4, "subq $0x28, %rsp\n\t" // shadow register space and 1 parameter "movq %r9, 0x20(%rsp)\n\t" "movq %r8, %r9\n\t" // shift over arguments "movq %rdx, %r8\n\t" "movq %rcx, %rdx\n\t" "movq %r10, %rcx\n\t" // add This pointer "call *%r11\n\t" "addq $0x28, %rsp\n\t" "ret"); // handles "this" and up to 9 parameters __ASM_GLOBAL_FUNC(call_flat_method9, "subq $0x58, %rsp\n\t" // shadow register space and 6 parameters "movq %r9, 0x20(%rsp)\n\t" "movq 0x80(%rsp), %rax\n\t" // copy parameters "movq %rax, 0x28(%rsp)\n\t" "movq 0x88(%rsp), %rax\n\t" "movq %rax, 0x30(%rsp)\n\t" "movq 0x90(%rsp), %rax\n\t" "movq %rax, 0x38(%rsp)\n\t" "movq 0x98(%rsp), %rax\n\t" "movq %rax, 0x40(%rsp)\n\t" "movq 0xa0(%rsp), %rax\n\t" "movq %rax, 0x48(%rsp)\n\t" "movq %r8, %r9\n\t" // shift over arguments "movq %rdx, %r8\n\t" "movq %rcx, %rdx\n\t" "movq %r10, %rcx\n\t" // add This pointer "call *%r11\n\t" "addq $0x58, %rsp\n\t" "ret"); #endif