Proton/wineopenxr/openxr.c
Billy Laws 4ef5ee6889 wineopenxr: PE split.
CW-Bug-Id: #25198
2025-04-29 12:25:48 +03:00

439 lines
16 KiB
C

#if 0
#pragma makedep unix
#endif
#define _GNU_SOURCE
#include <dlfcn.h>
#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;
}