#if 0 #pragma makedep unix #endif #define _GNU_SOURCE #include #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winnt.h" #include "wine/server_protocol.h" #include "openxr_private.h" WINE_DEFAULT_DEBUG_CHANNEL(openxr); static struct { const char *win32_ext, *linux_ext; BOOL remove_original; BOOL force_enable; } substitute_extensions[] = { {"XR_KHR_D3D11_enable", "XR_KHR_vulkan_enable"}, {"XR_KHR_D3D12_enable", "XR_KHR_vulkan_enable"}, {"XR_KHR_win32_convert_performance_counter_time", "XR_KHR_convert_timespec_time", TRUE, TRUE}, }; struct openxr_instance_funcs g_xr_host_instance_dispatch_table; VkDevice (*get_native_VkDevice)(VkDevice); VkInstance (*get_native_VkInstance)(VkInstance); VkPhysicalDevice (*get_native_VkPhysicalDevice)(VkPhysicalDevice); VkPhysicalDevice (*get_wrapped_VkPhysicalDevice)(VkInstance, VkPhysicalDevice); VkQueue (*get_native_VkQueue)(VkQueue); XrResult WINAPI wine_xrCreateInstance(const XrInstanceCreateInfo *createInfo, XrInstance *instance) { XrResult res; uint32_t i, j, count = 0; XrInstanceCreateInfo our_createInfo; const char *ext_name; const char **new_list; TRACE("%p, %p\n", createInfo, instance); TRACE("Incoming extensions:\n"); for (i = 0; i < createInfo->enabledExtensionCount; ++i) { TRACE(" -%s\n", createInfo->enabledExtensionNames[i]); } new_list = malloc(createInfo->enabledExtensionCount * sizeof(*new_list)); /* remove win32 extensions */ for (i = 0; i < createInfo->enabledExtensionCount; ++i) { ext_name = createInfo->enabledExtensionNames[i]; for (j = 0; j < ARRAY_SIZE(substitute_extensions); ++j) { if (!strcmp(ext_name, substitute_extensions[j].win32_ext)) { if (substitute_extensions[j].force_enable) { ext_name = NULL; } else { ext_name = substitute_extensions[j].linux_ext; } break; } } if (ext_name) { new_list[count++] = ext_name; } } our_createInfo = *createInfo; our_createInfo.enabledExtensionNames = (const char *const *)new_list; our_createInfo.enabledExtensionCount = count; createInfo = &our_createInfo; TRACE("Enabled extensions:\n"); for (i = 0; i < createInfo->enabledExtensionCount; ++i) { TRACE(" -%s\n", createInfo->enabledExtensionNames[i]); } res = xrCreateInstance(createInfo, instance); if (res != XR_SUCCESS) { WARN("xrCreateInstance failed: %d\n", res); goto cleanup; } #define USE_XR_FUNC(x) \ xrGetInstanceProcAddr(*instance, #x, (PFN_xrVoidFunction *)&g_xr_host_instance_dispatch_table.p_##x); ALL_XR_INSTANCE_FUNCS() #undef USE_XR_FUNC cleanup: free((void *)our_createInfo.enabledExtensionNames); return res; } XrResult WINAPI wine_xrCreateSession(XrInstance instance, const XrSessionCreateInfo *createInfo, XrSession *session) { wine_XrInstance *wine_instance = wine_instance_from_handle(instance); XrResult res; XrSessionCreateInfo our_create_info; XrGraphicsBindingVulkanKHR our_vk_binding; if (createInfo->next) { switch (((XrBaseInStructure *)createInfo->next)->type) { case XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR /* == XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR */: { const XrGraphicsBindingVulkanKHR *their_vk_binding = (const XrGraphicsBindingVulkanKHR *)createInfo->next; our_vk_binding = *their_vk_binding; our_vk_binding.instance = get_native_VkInstance(their_vk_binding->instance); our_vk_binding.physicalDevice = get_native_VkPhysicalDevice(their_vk_binding->physicalDevice); our_vk_binding.device = get_native_VkDevice(their_vk_binding->device); our_create_info = *createInfo; our_create_info.next = &our_vk_binding; createInfo = &our_create_info; break; } default: WARN("Unhandled graphics binding type: %d\n", ((XrBaseInStructure *)createInfo->next)->type); break; } } res = g_xr_host_instance_dispatch_table.p_xrCreateSession(wine_instance->host_instance, createInfo, session); if (res != XR_SUCCESS) { WARN("xrCreateSession failed: %d\n", res); return res; } return XR_SUCCESS; } XrResult WINAPI wine_xrCreateSwapchain(XrSession session, const XrSwapchainCreateInfo *createInfo, XrSwapchain *swapchain) { return g_xr_host_instance_dispatch_table.p_xrCreateSwapchain(wine_session_from_handle(session)->host_session, createInfo, swapchain); } XrResult WINAPI wine_xrEnumerateInstanceExtensionProperties(const char *layerName, uint32_t propertyCapacityInput, uint32_t *propertyCountOutput, XrExtensionProperties *properties) { uint32_t i, j, dst, count, extra_extensions_count; XrResult res; TRACE("\n"); res = xrEnumerateInstanceExtensionProperties(layerName, propertyCapacityInput, propertyCountOutput, properties); if (res != XR_SUCCESS) { return res; } if (!properties) { extra_extensions_count = 0; for (i = 0; i < ARRAY_SIZE(substitute_extensions); ++i) { if (!substitute_extensions[i].remove_original || substitute_extensions[i].force_enable) { ++extra_extensions_count; } } *propertyCountOutput += extra_extensions_count; TRACE("%u extensions.\n", *propertyCountOutput); return XR_SUCCESS; } count = *propertyCountOutput; for (i = 0; i < count; ++i) { for (j = 0; j < ARRAY_SIZE(substitute_extensions); ++j) { if (!strcmp(properties[i].extensionName, substitute_extensions[j].linux_ext)) { if (substitute_extensions[j].force_enable) { FIXME("Force enabled extension %s already supported by the runtime.\n", substitute_extensions[j].linux_ext); substitute_extensions[j].force_enable = FALSE; } if (substitute_extensions[j].remove_original) { dst = i; } else { dst = (*propertyCountOutput)++; } strcpy(properties[dst].extensionName, substitute_extensions[j].win32_ext); } } } for (j = 0; j < ARRAY_SIZE(substitute_extensions); ++j) { if (substitute_extensions[j].force_enable) { strcpy(properties[*propertyCountOutput].extensionName, substitute_extensions[j].win32_ext); ++*propertyCountOutput; } } TRACE("Enumerated extensions:\n"); for (i = 0; i < *propertyCountOutput; ++i) { TRACE(" -%s\n", properties[i].extensionName); } return XR_SUCCESS; } XrResult WINAPI wine_xrGetVulkanGraphicsDeviceKHR(XrInstance instance, XrSystemId systemId, VkInstance vkInstance, VkPhysicalDevice *vkPhysicalDevice) { XrResult res; TRACE("%p, 0x%s, %p, %p\n", instance, wine_dbgstr_longlong(systemId), vkInstance, vkPhysicalDevice); res = g_xr_host_instance_dispatch_table.p_xrGetVulkanGraphicsDeviceKHR( wine_instance_from_handle(instance)->host_instance, systemId, get_native_VkInstance(vkInstance), vkPhysicalDevice); *vkPhysicalDevice = get_wrapped_VkPhysicalDevice(vkInstance, *vkPhysicalDevice); return res; } XrResult WINAPI wine_xrGetVulkanGraphicsDevice2KHR(XrInstance instance, const XrVulkanGraphicsDeviceGetInfoKHR *getInfo, VkPhysicalDevice *vulkanPhysicalDevice) { XrVulkanGraphicsDeviceGetInfoKHR our_getinfo; XrResult res; TRACE("instance %p, getInfo %p, vulkanPhysicalDevice %p.\n", instance, getInfo, vulkanPhysicalDevice); if (getInfo->next) { WARN("Unsupported chained structure %p.\n", getInfo->next); } our_getinfo = *getInfo; our_getinfo.vulkanInstance = get_native_VkInstance(our_getinfo.vulkanInstance); res = g_xr_host_instance_dispatch_table.p_xrGetVulkanGraphicsDevice2KHR( wine_instance_from_handle(instance)->host_instance, &our_getinfo, vulkanPhysicalDevice); if (res == XR_SUCCESS) { *vulkanPhysicalDevice = get_wrapped_VkPhysicalDevice(getInfo->vulkanInstance, *vulkanPhysicalDevice); } return res; } XrResult WINAPI wine_xrGetVulkanDeviceExtensionsKHR(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t *bufferCountOutput, char *buffer) { wine_XrInstance *wine_instance = wine_instance_from_handle(instance); XrResult res; uint32_t buf_len = 0; char *buf; TRACE("%p, 0x%s, %u, %p, %p\n", instance, wine_dbgstr_longlong(systemId), bufferCapacityInput, bufferCountOutput, buffer); if (!getenv(WINE_VULKAN_DEVICE_EXTENSION_NAME)) { return g_xr_host_instance_dispatch_table.p_xrGetVulkanDeviceExtensionsKHR( wine_instance->host_instance, systemId, bufferCapacityInput, bufferCountOutput, buffer); } if (bufferCapacityInput == 0) { *bufferCountOutput = sizeof(WINE_VULKAN_DEVICE_EXTENSION_NAME); return XR_SUCCESS; } if (bufferCapacityInput < sizeof(WINE_VULKAN_DEVICE_EXTENSION_NAME)) { *bufferCountOutput = sizeof(WINE_VULKAN_DEVICE_EXTENSION_NAME); return XR_ERROR_SIZE_INSUFFICIENT; } res = g_xr_host_instance_dispatch_table.p_xrGetVulkanDeviceExtensionsKHR(wine_instance->host_instance, systemId, 0, &buf_len, NULL); if (res != XR_SUCCESS) { WARN("xrGetVulkanDeviceExtensionsKHR failed: %d\n", res); return res; } buf = malloc(buf_len); res = g_xr_host_instance_dispatch_table.p_xrGetVulkanDeviceExtensionsKHR(wine_instance->host_instance, systemId, buf_len, &buf_len, buf); if (res != XR_SUCCESS) { WARN("xrGetVulkanDeviceExtensionsKHR failed: %d\n", res); free(buf); return res; } TRACE("got device extensions: %s\n", buf); setenv(WINE_VULKAN_DEVICE_VARIABLE, buf, 1); free(buf); memcpy(buffer, WINE_VULKAN_DEVICE_EXTENSION_NAME, sizeof(WINE_VULKAN_DEVICE_EXTENSION_NAME)); *bufferCountOutput = sizeof(WINE_VULKAN_DEVICE_EXTENSION_NAME); return XR_SUCCESS; } XrResult WINAPI wine_xrGetVulkanInstanceExtensionsKHR(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t *bufferCountOutput, char *buffer) { static const char win32_surface[] = "VK_KHR_surface VK_KHR_win32_surface"; XrResult res; uint32_t lin_len; TRACE("%p, 0x%s, %u, %p, %p\n", instance, wine_dbgstr_longlong(systemId), bufferCapacityInput, bufferCountOutput, buffer); /* Linux SteamVR does not return xlib_surface, but Windows SteamVR _does_ * return win32_surface. Some games (including hello_xr) depend on that, so * add it here. */ res = g_xr_host_instance_dispatch_table.p_xrGetVulkanInstanceExtensionsKHR( wine_instance_from_handle(instance)->host_instance, systemId, bufferCapacityInput, bufferCountOutput, buffer); if (res == XR_SUCCESS) { if (bufferCapacityInput > 0) { /* *bufferCountOutput is not required to (and sometimes does not) contain the offset to the NUL byte */ lin_len = strlen(buffer) + 1; if (bufferCapacityInput < lin_len + sizeof(win32_surface)) { return XR_ERROR_SIZE_INSUFFICIENT; } buffer[lin_len - 1] = ' '; memcpy(&buffer[lin_len], win32_surface, sizeof(win32_surface)); TRACE("returning: %s\n", buffer); *bufferCountOutput = lin_len + sizeof(win32_surface); } else { *bufferCountOutput += sizeof(win32_surface) /* NUL byte included for required ' ' */; } } return res; } static VkResult WINAPI vk_create_instance_callback(const VkInstanceCreateInfo *create_info, const VkAllocationCallbacks *allocator, VkInstance *vk_instance, void *(*pfnGetInstanceProcAddr)(VkInstance, const char *), void *context) { struct vk_create_callback_context *c = context; XrVulkanInstanceCreateInfoKHR our_create_info; VkInstanceCreateInfo our_vulkan_create_info; const char **enabled_extensions = NULL; unsigned int i; VkResult ret; our_create_info = *(const XrVulkanInstanceCreateInfoKHR *)c->create_info; our_create_info.pfnGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)pfnGetInstanceProcAddr; our_create_info.vulkanCreateInfo = create_info; our_create_info.vulkanAllocator = allocator; for (i = 0; i < create_info->enabledExtensionCount; ++i) { if (!strcmp(create_info->ppEnabledExtensionNames[i], "VK_KHR_surface")) { break; } } if (i == create_info->enabledExtensionCount) { our_vulkan_create_info = *create_info; our_create_info.vulkanCreateInfo = &our_vulkan_create_info; enabled_extensions = malloc((create_info->enabledExtensionCount + 2) * sizeof(*enabled_extensions)); memcpy(enabled_extensions, create_info->ppEnabledExtensionNames, create_info->enabledExtensionCount * sizeof(*enabled_extensions)); enabled_extensions[our_vulkan_create_info.enabledExtensionCount++] = "VK_KHR_surface"; enabled_extensions[our_vulkan_create_info.enabledExtensionCount++] = "VK_KHR_xlib_surface"; our_vulkan_create_info.ppEnabledExtensionNames = enabled_extensions; } c->ret = g_xr_host_instance_dispatch_table.p_xrCreateVulkanInstanceKHR( wine_instance_from_handle(c->wine_instance)->host_instance, &our_create_info, vk_instance, &ret); free(enabled_extensions); return ret; } static VkResult WINAPI vk_create_device_callback(VkPhysicalDevice phys_dev, const VkDeviceCreateInfo *create_info, const VkAllocationCallbacks *allocator, VkDevice *vk_device, void *(*pfnGetInstanceProcAddr)(VkInstance, const char *), void *context) { /* Only Unix calls here, called from the Unix side. */ struct vk_create_callback_context *c = context; XrVulkanDeviceCreateInfoKHR our_create_info; VkResult ret; our_create_info = *(const XrVulkanDeviceCreateInfoKHR *)c->create_info; our_create_info.pfnGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)pfnGetInstanceProcAddr; our_create_info.vulkanPhysicalDevice = phys_dev; our_create_info.vulkanCreateInfo = create_info; our_create_info.vulkanAllocator = allocator; c->ret = g_xr_host_instance_dispatch_table.p_xrCreateVulkanDeviceKHR( wine_instance_from_handle(c->wine_instance)->host_instance, &our_create_info, vk_device, &ret); return ret; } NTSTATUS init_openxr(void *args) { struct init_openxr_params *params = args; NTSTATUS status; unixlib_handle_t unix_funcs; Dl_info info; void *unix_handle; status = NtQueryVirtualMemory(GetCurrentProcess(), params->winevulkan, (MEMORY_INFORMATION_CLASS)1000 /*MemoryWineUnixFuncs*/, &unix_funcs, sizeof(unix_funcs), NULL); if (status) { ERR("NtQueryVirtualMemory status %#x.\n", (int)status); return status; } if (!dladdr((void *)(ULONG_PTR)unix_funcs, &info)) { ERR("dladdr failed.\n"); return STATUS_INVALID_PARAMETER; } TRACE("path %s.\n", info.dli_fname); if (!(unix_handle = dlopen(info.dli_fname, RTLD_NOW))) { ERR("dlopen failed.\n"); return STATUS_INVALID_PARAMETER; } #define L(name) \ if (!(name = dlsym(unix_handle, "__wine_" #name))) \ ERR("%s not found.\n", #name); 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); params->create_instance_callback = (UINT64)&vk_create_instance_callback; params->create_device_callback = (UINT64)&vk_create_device_callback; return STATUS_SUCCESS; }