From f7c44ceee95a7b2e9c2ce9028f10ccb215732e3a Mon Sep 17 00:00:00 2001 From: GloriousEggroll Date: Tue, 4 Jun 2024 10:51:05 -0600 Subject: [PATCH] wineopenxr: import upstream changes --- wineopenxr/openxr.c | 465 ++++++++++++++++++++++++++++-- wineopenxr/openxr_private.h | 33 +++ wineopenxr/vkd3d-proton-interop.h | 186 ++++++++++++ 3 files changed, 653 insertions(+), 31 deletions(-) create mode 100644 wineopenxr/vkd3d-proton-interop.h diff --git a/wineopenxr/openxr.c b/wineopenxr/openxr.c index 9a3e7a4172..06942ad8aa 100644 --- a/wineopenxr/openxr.c +++ b/wineopenxr/openxr.c @@ -28,6 +28,7 @@ #include "wine/vulkan.h" #define VULKAN_H_ 1// tell dxvk-interop not to include vulkan.h #include "dxvk-interop.h" +#include "vkd3d-proton-interop.h" #undef WINE_VK_HOST #define XR_USE_GRAPHICS_API_D3D11 1 #define XR_USE_GRAPHICS_API_D3D12 1 @@ -79,6 +80,7 @@ static struct 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}, }; @@ -682,7 +684,6 @@ XrResult WINAPI wine_xrEnumerateInstanceExtensionProperties(const char *layerNam else dst = (*propertyCountOutput)++; strcpy(properties[dst].extensionName, substitute_extensions[j].win32_ext); - break; } } } @@ -790,9 +791,13 @@ XrResult WINAPI wine_xrGetD3D11GraphicsRequirementsKHR(XrInstance instance, XrResult WINAPI wine_xrGetD3D12GraphicsRequirementsKHR(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D12KHR *graphicsRequirements) { - WINE_FIXME("unimplemented\n"); - /* FIXME */ - return XR_ERROR_INITIALIZATION_FAILED; + XrGraphicsRequirementsD3D11KHR requirements; + XrResult result = wine_xrGetD3D11GraphicsRequirementsKHR(instance, systemId, &requirements); + if (result != XR_SUCCESS) + return result; + graphicsRequirements->adapterLuid = requirements.adapterLuid; + graphicsRequirements->minFeatureLevel = D3D_FEATURE_LEVEL_11_0; + return XR_SUCCESS; } XrResult WINAPI wine_xrGetInstanceProcAddr(XrInstance instance, const char *fn_name, PFN_xrVoidFunction *out_fn) @@ -907,6 +912,13 @@ XrResult WINAPI wine_xrDestroyInstance(XrInstance instance) if(wine_instance->dxvk_device) wine_instance->dxvk_device->lpVtbl->Release(wine_instance->dxvk_device); + if (wine_instance->d3d12_device) + { + wine_instance->p_vkDestroyCommandPool(wine_instance->vk_device, wine_instance->vk_command_pool, NULL); + wine_instance->d3d12_device->lpVtbl->Release(wine_instance->d3d12_device); + wine_instance->d3d12_queue->lpVtbl->Release(wine_instance->d3d12_queue); + } + heap_free(wine_instance); return XR_SUCCESS; @@ -1041,6 +1053,75 @@ XrResult WINAPI wine_xrCreateSession(XrInstance instance, const XrSessionCreateI break; } + case XR_TYPE_GRAPHICS_BINDING_D3D12_KHR: + { + const XrGraphicsBindingD3D12KHR *their_d3d12_binding = createInfo->next; + VkCommandPoolCreateInfo command_pool_create_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = 0, + .pNext = NULL, + }; + HRESULT hr; + UINT32 queue_index; + VkQueueFlags queue_flags; + ID3D12DeviceExt1 *device_ext; + hr = ID3D12Device_QueryInterface(their_d3d12_binding->device, &IID_ID3D12DXVKInteropDevice, (void**)&wine_instance->d3d12_device); + if (FAILED(hr)) + { + WINE_WARN("Given ID3D12Device doesn't support ID3D12DXVKInteropDevice. Only vkd3d-proton is supported.\n"); + return XR_ERROR_VALIDATION_FAILURE; + } + hr = ID3D12Device_QueryInterface(their_d3d12_binding->device, &IID_ID3D12DeviceExt1, (void **)&device_ext); + if (FAILED(hr)) + { + WINE_WARN("Given ID3D12Device doesn't support ID3D12DeviceExt1. Only vkd3d-proton is supported.\n"); + return XR_ERROR_VALIDATION_FAILURE; + } + + our_vk_binding.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR; + our_vk_binding.next = NULL; + + wine_instance->d3d12_queue = their_d3d12_binding->queue; + their_d3d12_binding->queue->lpVtbl->AddRef(their_d3d12_binding->queue); + + wine_instance->d3d12_device->lpVtbl->GetVulkanHandles(wine_instance->d3d12_device, + &our_vk_binding.instance, &our_vk_binding.physicalDevice, &our_vk_binding.device); + device_ext->lpVtbl->GetVulkanQueueInfoEx(device_ext, their_d3d12_binding->queue, + &wine_instance->vk_queue, &queue_index, &queue_flags, &our_vk_binding.queueFamilyIndex); + device_ext->lpVtbl->Release(device_ext); + + wine_instance->vk_device = our_vk_binding.device = get_native_VkDevice(our_vk_binding.device); + wine_instance->vk_queue = get_native_VkQueue(wine_instance->vk_queue); + our_vk_binding.queueIndex = queue_index; + + our_vk_binding.instance = get_native_VkInstance(our_vk_binding.instance); + + if ((res = do_vulkan_init(wine_instance, our_vk_binding.instance)) != XR_SUCCESS) + return res; + + if (wine_instance->vk_phys_dev != get_native_VkPhysicalDevice(our_vk_binding.physicalDevice)) + WINE_WARN("VK physical device does not match that from xrGetVulkanGraphicsDeviceKHR.\n"); + + our_vk_binding.physicalDevice = wine_instance->vk_phys_dev; + + our_create_info = *createInfo; + our_create_info.next = &our_vk_binding; + createInfo = &our_create_info; + +#define X(proc) wine_instance->p_##proc = (void *)vkGetDeviceProcAddr(our_vk_binding.device, #proc); + VK_PROCS +#undef X + command_pool_create_info.queueFamilyIndex = our_vk_binding.queueFamilyIndex; + if (wine_instance->p_vkCreateCommandPool(wine_instance->vk_device, &command_pool_create_info, NULL, &wine_instance->vk_command_pool) != VK_SUCCESS) + { + WINE_WARN("vkCreateCommandPool failed\n"); + return XR_ERROR_RUNTIME_FAILURE; + } + + session_type = SESSION_TYPE_D3D12; + + break; + } default: WINE_WARN("Unhandled graphics binding type: %d\n", ((XrBaseInStructure *)createInfo->next)->type); break; @@ -1455,6 +1536,29 @@ XrResult WINAPI wine_xrGetSystem(XrInstance instance, const XrSystemGetInfo *get return res; } +static BOOL is_vulkan_format_depth(VkFormat format) +{ + switch(format){ + case VK_FORMAT_B8G8R8A8_SRGB: + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_R8G8B8A8_SRGB: + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R32G32B32A32_SFLOAT: + case VK_FORMAT_R32G32B32_SFLOAT: + case VK_FORMAT_R16G16B16A16_SFLOAT: + return FALSE; + case VK_FORMAT_D32_SFLOAT: + case VK_FORMAT_D16_UNORM: + case VK_FORMAT_D24_UNORM_S8_UINT: + case VK_FORMAT_D32_SFLOAT_S8_UINT: + return TRUE; + + default: + WINE_WARN("Unknown vulkan format %#x", format); + return FALSE; + } +} + int64_t map_format_dxgi_to_vulkan(int64_t format) { switch(format){ @@ -1549,7 +1653,7 @@ XrResult WINAPI wine_xrEnumerateSwapchainFormats(XrSession session, uint32_t for WINE_TRACE("%p, %u, %p, %p\n", session, formatCapacityInput, formatCountOutput, formats); - if (wine_session->session_type != SESSION_TYPE_D3D11) + if (wine_session->session_type != SESSION_TYPE_D3D11 && wine_session->session_type != SESSION_TYPE_D3D12) return xrEnumerateSwapchainFormats(wine_session->session, formatCapacityInput, formatCountOutput, formats); res = xrEnumerateSwapchainFormats(wine_session->session, 0, &real_format_count, NULL); @@ -1597,7 +1701,8 @@ XrResult WINAPI wine_xrCreateSwapchain(XrSession session, const XrSwapchainCreat wine_swapchain = heap_alloc_zero(sizeof(*wine_swapchain)); wine_swapchain->create_info = *createInfo; - if(wine_session->session_type == SESSION_TYPE_D3D11){ + if(wine_session->session_type == SESSION_TYPE_D3D11 || wine_session->session_type == SESSION_TYPE_D3D12){ + BOOL format_is_depth; our_createInfo = *createInfo; our_createInfo.format = map_format_dxgi_to_vulkan(createInfo->format); if(our_createInfo.format == VK_FORMAT_UNDEFINED){ @@ -1606,6 +1711,14 @@ XrResult WINAPI wine_xrCreateSwapchain(XrSession session, const XrSwapchainCreat return XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED; } createInfo = &our_createInfo; + + format_is_depth = is_vulkan_format_depth(our_createInfo.format); + if ((createInfo->usageFlags & XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT) && format_is_depth){ + WINE_WARN("Swapchain has a color attachment usage, but it's format is for depth\n"); + } + if ((createInfo->usageFlags & XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !format_is_depth){ + WINE_WARN("Swapchain has a depth/stencil usage, but it's format is for color\n"); + } } res = xrCreateSwapchain(((wine_XrSession *)session)->session, createInfo, &wine_swapchain->swapchain); @@ -1624,6 +1737,25 @@ XrResult WINAPI wine_xrCreateSwapchain(XrSession session, const XrSwapchainCreat return XR_SUCCESS; } +static void release_d3d12_resources(wine_XrSwapchain *wine_swapchain, uint32_t image_count) +{ + wine_XrInstance *wine_instance = wine_swapchain->wine_session->wine_instance; + XrSwapchainImageD3D12KHR *d3d12_images = (XrSwapchainImageD3D12KHR *)wine_swapchain->images; + UINT i; + if (!image_count) + return; + + for (i = 0; i < image_count; i++) + if (d3d12_images[i].texture) + d3d12_images[i].texture->lpVtbl->Release(d3d12_images[i].texture); + wine_instance->p_vkFreeCommandBuffers(wine_instance->vk_device, wine_instance->vk_command_pool, image_count, wine_swapchain->cmd_release); + wine_instance->p_vkFreeCommandBuffers(wine_instance->vk_device, wine_instance->vk_command_pool, image_count, wine_swapchain->cmd_acquire); + heap_free(wine_swapchain->cmd_release); + heap_free(wine_swapchain->cmd_acquire); + heap_free(wine_swapchain->acquired); + heap_free(wine_swapchain->acquired_indices); +} + XrResult WINAPI wine_xrDestroySwapchain(XrSwapchain swapchain) { wine_XrSwapchain *wine_swapchain = (wine_XrSwapchain *)swapchain; @@ -1631,12 +1763,23 @@ XrResult WINAPI wine_xrDestroySwapchain(XrSwapchain swapchain) WINE_TRACE("%p\n", swapchain); + if (wine_swapchain->image_count){ + UINT i; + if (wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D11){ + XrSwapchainImageD3D11KHR *d3d11_images = (XrSwapchainImageD3D11KHR *)wine_swapchain->images; + for (i = 0; i < wine_swapchain->image_count; i++) + d3d11_images[i].texture->lpVtbl->Release(d3d11_images[i].texture); + } else if (wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D12) + release_d3d12_resources(wine_swapchain, wine_swapchain->image_count); + heap_free(wine_swapchain->images); + wine_swapchain->image_count = 0; + } + res = xrDestroySwapchain(wine_swapchain->swapchain); if(res != XR_SUCCESS){ WINE_WARN("xrDestroySwapchain failed: %d\n", res); return res; } - heap_free(wine_swapchain); return XR_SUCCESS; @@ -1663,33 +1806,79 @@ static D3D11_USAGE d3d11usage_from_XrSwapchainUsageFlags(XrSwapchainUsageFlags f return ret; } +static VkResult record_transition_command(wine_XrInstance *instance, VkImage image, VkImageSubresourceRange subresource, + VkImageLayout from, VkImageLayout to, VkCommandBuffer *out_cmd) +{ + VkResult ret; + VkCommandBufferAllocateInfo command_buffer_allocate_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandPool = instance->vk_command_pool, + .commandBufferCount = 1, + }; + VkCommandBufferBeginInfo begin_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = 0, + }; + VkImageMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .oldLayout = from, + .newLayout = to, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange = subresource, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + }; + + *out_cmd = VK_NULL_HANDLE; + if (from == to){ + return VK_SUCCESS; + } + + if ((ret = instance->p_vkAllocateCommandBuffers(instance->vk_device, &command_buffer_allocate_info, out_cmd)) != VK_SUCCESS) + return ret; + if ((ret = instance->p_vkBeginCommandBuffer(*out_cmd, &begin_info)) != VK_SUCCESS) + return ret; + instance->p_vkCmdPipelineBarrier(*out_cmd, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, 0, NULL, 0, NULL, 1, &barrier); + return instance->p_vkEndCommandBuffer(*out_cmd); +} + XrResult WINAPI wine_xrEnumerateSwapchainImages(XrSwapchain swapchain, uint32_t imageCapacityInput, uint32_t *imageCountOutput, XrSwapchainImageBaseHeader *images) { wine_XrSwapchain *wine_swapchain = (wine_XrSwapchain *)swapchain; wine_XrInstance *wine_instance = wine_swapchain->wine_session->wine_instance; XrResult res; - XrSwapchainImageVulkanKHR *our_images = NULL; - XrSwapchainImageBaseHeader *their_images = images; + XrSwapchainImageVulkanKHR *our_vk = NULL; HRESULT hr; + size_t image_size = 0; uint32_t i; WINE_TRACE("%p, %u, %p, %p\n", swapchain, imageCapacityInput, imageCountOutput, images); - - if(images){ - if(wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D11){ - our_images = heap_alloc(sizeof(*our_images) * imageCapacityInput); - for(i = 0; i < imageCapacityInput; ++i){ - our_images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR; - } - images = (XrSwapchainImageBaseHeader *)our_images; + if (wine_swapchain->wine_session->session_type != SESSION_TYPE_D3D11 && wine_swapchain->wine_session->session_type != SESSION_TYPE_D3D12) + return xrEnumerateSwapchainImages(wine_swapchain->swapchain, imageCapacityInput, imageCountOutput, images); + + if (!wine_swapchain->image_count) { + uint32_t image_count; + res = xrEnumerateSwapchainImages(wine_swapchain->swapchain, 0, &image_count, NULL); + if (res != XR_SUCCESS) + return res; + + our_vk = heap_alloc(sizeof(*our_vk) * image_count); + for(i = 0; i < image_count; ++i){ + our_vk[i].type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR; } - } - res = xrEnumerateSwapchainImages(wine_swapchain->swapchain, imageCapacityInput, imageCountOutput, images); + res = xrEnumerateSwapchainImages(wine_swapchain->swapchain, image_count, &image_count, (XrSwapchainImageBaseHeader *)our_vk); + if (res != XR_SUCCESS){ + heap_free(our_vk); + return res; + } - if(images && res == XR_SUCCESS){ if(wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D11){ - XrSwapchainImageD3D11KHR *their_d3d11; + XrSwapchainImageD3D11KHR *our_d3d11; D3D11_TEXTURE2D_DESC1 desc; desc.Width = wine_swapchain->create_info.width; @@ -1707,21 +1896,129 @@ XrResult WINAPI wine_xrEnumerateSwapchainImages(XrSwapchain swapchain, uint32_t desc.MiscFlags = 0; desc.TextureLayout = D3D11_TEXTURE_LAYOUT_UNDEFINED; - their_d3d11 = (XrSwapchainImageD3D11KHR *)their_images; - for(i = 0; i < *imageCountOutput; ++i){ + our_d3d11 = heap_alloc(sizeof(XrSwapchainImageD3D11KHR) * image_count); + for(i = 0; i < image_count; ++i){ hr = wine_instance->dxvk_device->lpVtbl->CreateTexture2DFromVkImage(wine_instance->dxvk_device, - &desc, our_images[i].image, &their_d3d11[i].texture); + &desc, our_vk[i].image, &our_d3d11[i].texture); if(FAILED(hr)){ + UINT j; + for (j = 0; j < i; ++j){ + our_d3d11[i].texture->lpVtbl->Release(our_d3d11[i].texture); + } + heap_free(our_d3d11); + heap_free(our_vk); WINE_WARN("Failed to create DXVK texture from VkImage: %08x\n", hr); - return XR_ERROR_INSTANCE_LOST; + return XR_ERROR_RUNTIME_FAILURE; } - WINE_TRACE("Successfully allocated texture %p\n", their_d3d11[i].texture); + WINE_TRACE("Successfully allocated texture %p\n", our_d3d11[i].texture); + } + wine_swapchain->images = (XrSwapchainImageBaseHeader *)our_d3d11; + } else if(wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D12){ + XrSwapchainImageD3D12KHR *our_d3d12; + D3D12_RESOURCE_DESC1 desc; + ID3D12DeviceExt1 *device_ext; + HRESULT hr = wine_instance->d3d12_device->lpVtbl->QueryInterface(wine_instance->d3d12_device, &IID_ID3D12DeviceExt1, (void **)&device_ext); + BOOL format_is_depth = is_vulkan_format_depth(map_format_dxgi_to_vulkan(wine_swapchain->create_info.format)); + BOOL succeeded = TRUE; + const D3D12_RESOURCE_STATES incoming_state = format_is_depth ? D3D12_RESOURCE_STATE_DEPTH_WRITE : D3D12_RESOURCE_STATE_RENDER_TARGET; + const VkImageLayout vk_layout = format_is_depth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + const VkImageSubresourceRange subresource = + { + .layerCount = wine_swapchain->create_info.arraySize, + .baseArrayLayer = 0, + .levelCount = wine_swapchain->create_info.mipCount, + .baseMipLevel = 0, + .aspectMask = format_is_depth ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) : VK_IMAGE_ASPECT_COLOR_BIT, + }; + if (FAILED(hr)) + { + WINE_ERR("Cannot get vkd3d-proton interface: %08x\n", hr); + return XR_ERROR_VALIDATION_FAILURE; + } + + desc.Alignment = 0; + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc.Width = wine_swapchain->create_info.width; + desc.Height = wine_swapchain->create_info.height; + desc.MipLevels = wine_swapchain->create_info.mipCount; + desc.DepthOrArraySize = wine_swapchain->create_info.arraySize; + desc.Format = wine_swapchain->create_info.format; + WINE_TRACE("creating vkd3d-proton texture with dxgi format %d (%x)\n", + desc.Format, desc.Format); + desc.SampleDesc.Count = wine_swapchain->create_info.sampleCount; + desc.SampleDesc.Quality = 0; + desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + desc.Flags = 0; + if (!format_is_depth) + desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + else + { + desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + if (!(wine_swapchain->create_info.usageFlags & XR_SWAPCHAIN_USAGE_SAMPLED_BIT)) + desc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + } + if (wine_swapchain->create_info.usageFlags & XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT) + desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + + our_d3d12 = heap_alloc_zero(sizeof(XrSwapchainImageD3D12KHR) * image_count); + for(i = 0; i < image_count; ++i) + { + hr = device_ext->lpVtbl->CreateResourceFromBorrowedHandle(device_ext, + &desc, our_vk[i].image, &our_d3d12[i].texture); + if(FAILED(hr)) + { + WINE_ERR("Failed to create vkd3d-proton texture from VkImage: %08x\n", hr); + succeeded = FALSE; + break; + } + WINE_TRACE("Successfully allocated texture %p\n", our_d3d12[i].texture); + } + device_ext->lpVtbl->Release(device_ext); + + if (succeeded) + { + wine_swapchain->acquired = heap_alloc_zero(sizeof(BOOL) * image_count); + wine_swapchain->acquired_indices = heap_alloc(sizeof(uint32_t) * (image_count + 1)); + wine_swapchain->acquired_count = wine_swapchain->acquired_start = 0; + wine_swapchain->images = (XrSwapchainImageBaseHeader *)our_d3d12; + + wine_swapchain->cmd_release = heap_alloc_zero(sizeof(VkCommandBuffer) * image_count); + wine_swapchain->cmd_acquire = heap_alloc_zero(sizeof(VkCommandBuffer) * image_count); + for (i = 0; i < image_count; i++) + { + VkImageLayout d3d12_vk_layout; + wine_instance->d3d12_device->lpVtbl->GetVulkanImageLayout(wine_instance->d3d12_device, our_d3d12[i].texture, incoming_state, &d3d12_vk_layout); + if (record_transition_command(wine_instance, our_vk[i].image, subresource, d3d12_vk_layout, vk_layout, &wine_swapchain->cmd_release[i]) != VK_SUCCESS || + record_transition_command(wine_instance, our_vk[i].image, subresource, vk_layout, d3d12_vk_layout, &wine_swapchain->cmd_acquire[i]) != VK_SUCCESS) + { + WINE_ERR("Failed to create command buffer for layout transition\n"); + succeeded = FALSE; + break; + } + } + } + if (!succeeded) + { + release_d3d12_resources(wine_swapchain, image_count); + heap_free(wine_swapchain->images); + wine_swapchain->images = NULL; + heap_free(our_vk); + return XR_ERROR_RUNTIME_FAILURE; } } + heap_free(our_vk); + wine_swapchain->image_count = image_count; + WINE_TRACE("image count %d\n", image_count); } - heap_free(our_images); - return res; + *imageCountOutput = wine_swapchain->image_count; + if (wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D11){ + image_size = sizeof(XrSwapchainImageD3D11KHR); + } else if (wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D12){ + image_size = sizeof(XrSwapchainImageD3D12KHR); + } + memcpy(images, wine_swapchain->images, image_size * min(wine_swapchain->image_count, imageCapacityInput)); + return XR_SUCCESS; } static XrCompositionLayerBaseHeader *convert_XrCompositionLayer(wine_XrSession *wine_session, @@ -1855,6 +2152,7 @@ XrResult WINAPI wine_xrEndFrame(XrSession session, const XrFrameEndInfo *frameEn wine_XrSession *wine_session = (wine_XrSession *)session; uint32_t i, view_idx = 0, view_info_idx = 0; IDXGIVkInteropDevice2 *dxvk_device; + ID3D12DXVKInteropDevice *d3d12_device; XrFrameEndInfo our_frameEndInfo; XrResult res; @@ -1884,9 +2182,16 @@ XrResult WINAPI wine_xrEndFrame(XrSession session, const XrFrameEndInfo *frameEn dxvk_device->lpVtbl->FlushRenderingCommands(dxvk_device); dxvk_device->lpVtbl->LockSubmissionQueue(dxvk_device); } + else if ((d3d12_device = wine_session->wine_instance->d3d12_device)) + { + WINE_TRACE("Locking vkd3d-proton submission queue.\n"); + d3d12_device->lpVtbl->LockCommandQueue(d3d12_device, wine_session->wine_instance->d3d12_queue); + } res = xrEndFrame(((wine_XrSession *)session)->session, &our_frameEndInfo); if (dxvk_device) dxvk_device->lpVtbl->ReleaseSubmissionQueue(dxvk_device); + else if (d3d12_device) + d3d12_device->lpVtbl->UnlockCommandQueue(d3d12_device, wine_session->wine_instance->d3d12_queue); return res; } @@ -1895,45 +2200,143 @@ XrResult WINAPI wine_xrBeginFrame(XrSession session, const XrFrameBeginInfo *fra { wine_XrSession *wine_session = (wine_XrSession *)session; IDXGIVkInteropDevice2 *dxvk_device; + ID3D12DXVKInteropDevice *d3d12_device; XrResult ret; WINE_TRACE("%p, %p\n", session, frameBeginInfo); if ((dxvk_device = wine_session->wine_instance->dxvk_device)) dxvk_device->lpVtbl->LockSubmissionQueue(dxvk_device); + else if ((d3d12_device = wine_session->wine_instance->d3d12_device)) + d3d12_device->lpVtbl->LockCommandQueue(d3d12_device, wine_session->wine_instance->d3d12_queue); ret = xrBeginFrame(((wine_XrSession *)session)->session, frameBeginInfo); if (dxvk_device) dxvk_device->lpVtbl->ReleaseSubmissionQueue(dxvk_device); + else if (d3d12_device) + d3d12_device->lpVtbl->UnlockCommandQueue(d3d12_device, wine_session->wine_instance->d3d12_queue); return ret; } XrResult WINAPI wine_xrAcquireSwapchainImage(XrSwapchain swapchain, const XrSwapchainImageAcquireInfo *acquireInfo, uint32_t *index) { - wine_XrSession *wine_session = ((wine_XrSwapchain *)swapchain)->wine_session; + wine_XrSwapchain *wine_swapchain = (wine_XrSwapchain *)swapchain; + wine_XrSession *wine_session = wine_swapchain->wine_session; IDXGIVkInteropDevice2 *dxvk_device; + ID3D12DXVKInteropDevice *d3d12_device; XrResult ret; - WINE_TRACE("%p, %p, %p\n", swapchain, acquireInfo, index); + WINE_TRACE("%p, %p, %p image count %d, acquired %d\n", swapchain, acquireInfo, index, wine_swapchain->image_count, + wine_swapchain->acquired_count); + if ((dxvk_device = wine_session->wine_instance->dxvk_device)) dxvk_device->lpVtbl->LockSubmissionQueue(dxvk_device); + else if ((d3d12_device = wine_session->wine_instance->d3d12_device)) + { + if (wine_swapchain->acquired_count >= wine_swapchain->image_count) + { + WINE_WARN("Application has acquired all images but still tries to acquire more.\n"); + return XR_ERROR_CALL_ORDER_INVALID; + } + d3d12_device->lpVtbl->LockCommandQueue(d3d12_device, wine_session->wine_instance->d3d12_queue); + } ret = xrAcquireSwapchainImage(((wine_XrSwapchain *)swapchain)->swapchain, acquireInfo, index); if (dxvk_device) + { dxvk_device->lpVtbl->ReleaseSubmissionQueue(dxvk_device); + return ret; + } + + if (!d3d12_device) + return ret; + + if (ret == XR_SUCCESS) + { + if (!wine_swapchain->acquired[*index] && wine_swapchain->cmd_acquire[*index] != VK_NULL_HANDLE) + { + VkSubmitInfo submit_info = + { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &wine_swapchain->cmd_acquire[*index], + }; + + wine_session->wine_instance->p_vkQueueSubmit(wine_session->wine_instance->vk_queue, 1, &submit_info, VK_NULL_HANDLE); + } + if (!wine_swapchain->acquired[*index]) + { + uint32_t next = (wine_swapchain->acquired_start + wine_swapchain->acquired_count) % wine_swapchain->image_count; + wine_swapchain->acquired[*index] = TRUE; + wine_swapchain->acquired_indices[next] = *index; + wine_swapchain->acquired_count += 1; + } + else + WINE_WARN("the application acquired the same image (index %d) again!?", *index); + } + d3d12_device->lpVtbl->UnlockCommandQueue(d3d12_device, wine_session->wine_instance->d3d12_queue); return ret; } XrResult WINAPI wine_xrReleaseSwapchainImage(XrSwapchain swapchain, const XrSwapchainImageReleaseInfo *releaseInfo) { - wine_XrSession *wine_session = ((wine_XrSwapchain *)swapchain)->wine_session; + wine_XrSwapchain *wine_swapchain = (wine_XrSwapchain *)swapchain; + wine_XrSession *wine_session = wine_swapchain->wine_session; IDXGIVkInteropDevice2 *dxvk_device; + ID3D12DXVKInteropDevice *d3d12_device = wine_session->wine_instance->d3d12_device; XrResult ret; + uint32_t index; WINE_TRACE("%p, %p\n", swapchain, releaseInfo); if ((dxvk_device = wine_session->wine_instance->dxvk_device)) dxvk_device->lpVtbl->LockSubmissionQueue(dxvk_device); + else if (d3d12_device) + { + if (wine_swapchain->acquired_count == 0) + { + WINE_WARN("Application tried to release a swapchain image without having acquired it first.\n"); + return XR_ERROR_CALL_ORDER_INVALID; + } + + d3d12_device->lpVtbl->LockCommandQueue(d3d12_device, wine_session->wine_instance->d3d12_queue); + + index = wine_swapchain->acquired_indices[wine_swapchain->acquired_start]; + if (wine_swapchain->cmd_release[index] != VK_NULL_HANDLE) + { + VkSubmitInfo submit_info = + { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &wine_swapchain->cmd_release[index], + }; + + wine_session->wine_instance->p_vkQueueSubmit(wine_session->wine_instance->vk_queue, 1, &submit_info, VK_NULL_HANDLE); + } + } ret = xrReleaseSwapchainImage(((wine_XrSwapchain *)swapchain)->swapchain, releaseInfo); if (dxvk_device) + { dxvk_device->lpVtbl->ReleaseSubmissionQueue(dxvk_device); + return ret; + } + if (!d3d12_device) + return ret; + if (ret != XR_SUCCESS && wine_swapchain->cmd_release[index] != VK_NULL_HANDLE) + { + VkSubmitInfo submit_info = + { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &wine_swapchain->cmd_acquire[index], + }; + WINE_WARN("xrReleaseSwapchainImage failed, reverting layout transition\n"); + wine_session->wine_instance->p_vkQueueSubmit(wine_session->wine_instance->vk_queue, 1, &submit_info, VK_NULL_HANDLE); + } + if (ret == XR_SUCCESS) + { + wine_swapchain->acquired[index] = FALSE; + wine_swapchain->acquired_start = (wine_swapchain->acquired_start + 1) % wine_swapchain->image_count; + wine_swapchain->acquired_count -= 1; + } + d3d12_device->lpVtbl->UnlockCommandQueue(d3d12_device, wine_session->wine_instance->d3d12_queue); return ret; } diff --git a/wineopenxr/openxr_private.h b/wineopenxr/openxr_private.h index 7f1b7411c9..8cfada59f7 100644 --- a/wineopenxr/openxr_private.h +++ b/wineopenxr/openxr_private.h @@ -9,6 +9,22 @@ struct IDXGIVkInteropDevice2; typedef struct IDXGIVkInteropDevice2 IDXGIVkInteropDevice2; +typedef struct ID3D12DXVKInteropDevice ID3D12DXVKInteropDevice; + +#define VK_PROCS \ + X(vkAllocateCommandBuffers) \ + X(vkBeginCommandBuffer) \ + X(vkEndCommandBuffer) \ + X(vkQueueSubmit) \ + X(vkQueueWaitIdle) \ + X(vkFreeCommandBuffers) \ + X(vkCmdPipelineBarrier) \ + X(vkCreateCommandPool) \ + X(vkDestroyCommandPool) \ + X(vkDestroyFence) \ + X(vkCreateFence) \ + X(vkWaitForFences) \ + X(vkResetFences) typedef struct wine_XrInstance { XrInstance instance; @@ -20,6 +36,16 @@ typedef struct wine_XrInstance { XrSystemId systemId; IDXGIVkInteropDevice2 *dxvk_device; + ID3D12DXVKInteropDevice *d3d12_device; + ID3D12CommandQueue *d3d12_queue; + + /* For layout transitions for vkd3d-proton */ +#define X(proc) PFN_##proc p_##proc; + VK_PROCS +#undef X + VkDevice vk_device; + VkQueue vk_queue; + VkCommandPool vk_command_pool; } wine_XrInstance; union CompositionLayer; @@ -49,9 +75,16 @@ typedef struct wine_XrSession { typedef struct wine_XrSwapchain{ XrSwapchain swapchain; + XrSwapchainImageBaseHeader *images; + uint32_t image_count; + uint32_t acquired_count, acquired_start; + BOOL *acquired; + uint32_t *acquired_indices; struct wine_XrSession *wine_session; XrSwapchainCreateInfo create_info; + VkCommandBuffer *cmd_release; + VkCommandBuffer *cmd_acquire; } wine_XrSwapchain; struct openxr_func { diff --git a/wineopenxr/vkd3d-proton-interop.h b/wineopenxr/vkd3d-proton-interop.h new file mode 100644 index 0000000000..34c2cb122f --- /dev/null +++ b/wineopenxr/vkd3d-proton-interop.h @@ -0,0 +1,186 @@ +/*** Partially copied from autogenerated header by WIDL 6.4 from ../src-vkd3d-proton/include/vkd3d_device_vkd3d_ext.idl ***/ + +#pragma once + +#include +#include + +#ifndef __ID3D12DeviceExt1_FWD_DEFINED__ +#define __ID3D12DeviceExt1_FWD_DEFINED__ +typedef interface ID3D12DeviceExt1 ID3D12DeviceExt1; +#endif + +#ifndef __ID3D12DXVKInteropDevice_FWD_DEFINED__ +#define __ID3D12DXVKInteropDevice_FWD_DEFINED__ +typedef interface ID3D12DXVKInteropDevice ID3D12DXVKInteropDevice; +#endif + +/***************************************************************************** + * ID3D12DeviceExt1 interface + */ +#ifndef __ID3D12DeviceExt1_INTERFACE_DEFINED__ +#define __ID3D12DeviceExt1_INTERFACE_DEFINED__ +typedef struct D3D12_UAV_INFO D3D12_UAV_INFO; +typedef struct D3D12_VK_EXTENSION D3D12_VK_EXTENSION; +typedef struct D3D12_CUBIN_DATA_HANDLE D3D12_CUBIN_DATA_HANDLE; + +DEFINE_GUID(IID_ID3D12DeviceExt1, 0x099a73fd, 0x2199, 0x4f45, 0xbf,0x48, 0x0e,0xb8,0x6f,0x6f,0xdb,0x65); +typedef struct ID3D12DeviceExt1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + ID3D12DeviceExt1 *This, + REFIID riid, + void **object); + + ULONG (STDMETHODCALLTYPE *AddRef)( + ID3D12DeviceExt1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + ID3D12DeviceExt1 *This); + + /*** ID3D12DeviceExt methods ***/ + HRESULT (STDMETHODCALLTYPE *GetVulkanHandles)( + ID3D12DeviceExt1 *This, + VkInstance *vk_instance, + VkPhysicalDevice *vk_physical_device, + VkDevice *vk_device); + + BOOL (STDMETHODCALLTYPE *GetExtensionSupport)( + ID3D12DeviceExt1 *This, + D3D12_VK_EXTENSION extension); + + HRESULT (STDMETHODCALLTYPE *CreateCubinComputeShaderWithName)( + ID3D12DeviceExt1 *This, + const void *cubin_data, + UINT32 cubin_size, + UINT32 block_x, + UINT32 block_y, + UINT32 block_z, + const char *shader_name, + D3D12_CUBIN_DATA_HANDLE **handle); + + HRESULT (STDMETHODCALLTYPE *DestroyCubinComputeShader)( + ID3D12DeviceExt1 *This, + D3D12_CUBIN_DATA_HANDLE *handle); + + HRESULT (STDMETHODCALLTYPE *GetCudaTextureObject)( + ID3D12DeviceExt1 *This, + D3D12_CPU_DESCRIPTOR_HANDLE srv_handle, + D3D12_CPU_DESCRIPTOR_HANDLE sampler_handle, + UINT32 *cuda_texture_handle); + + HRESULT (STDMETHODCALLTYPE *GetCudaSurfaceObject)( + ID3D12DeviceExt1 *This, + D3D12_CPU_DESCRIPTOR_HANDLE uav_handle, + UINT32 *cuda_surface_handle); + + HRESULT (STDMETHODCALLTYPE *CaptureUAVInfo)( + ID3D12DeviceExt1 *This, + D3D12_UAV_INFO *uav_info); + + /*** ID3D12DeviceExt1 methods ***/ + HRESULT (STDMETHODCALLTYPE *CreateResourceFromBorrowedHandle)( + ID3D12DeviceExt1 *This, + const D3D12_RESOURCE_DESC1 *desc, + UINT64 vk_handle, + ID3D12Resource **resource); + + HRESULT (STDMETHODCALLTYPE *GetVulkanQueueInfoEx)( + ID3D12DeviceExt1 *This, + ID3D12CommandQueue *queue, + VkQueue *vk_queue, + UINT32 *vk_queue_index, + UINT32 *vk_queue_flags, + UINT32 *vk_queue_family); + + END_INTERFACE +} ID3D12DeviceExt1Vtbl; + +interface ID3D12DeviceExt1 { + CONST_VTBL ID3D12DeviceExt1Vtbl* lpVtbl; +}; +#endif + +/***************************************************************************** + * ID3D12DXVKInteropDevice interface + */ +#ifndef __ID3D12DXVKInteropDevice_INTERFACE_DEFINED__ +#define __ID3D12DXVKInteropDevice_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_ID3D12DXVKInteropDevice, 0x39da4e09, 0xbd1c, 0x4198, 0x9f,0xae, 0x86,0xbb,0xe3,0xbe,0x41,0xfd); +typedef struct ID3D12DXVKInteropDeviceVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + ID3D12DXVKInteropDevice *This, + REFIID riid, + void **object); + + ULONG (STDMETHODCALLTYPE *AddRef)( + ID3D12DXVKInteropDevice *This); + + ULONG (STDMETHODCALLTYPE *Release)( + ID3D12DXVKInteropDevice *This); + + /*** ID3D12DXVKInteropDevice methods ***/ + HRESULT (STDMETHODCALLTYPE *GetDXGIAdapter)( + ID3D12DXVKInteropDevice *This, + REFIID iid, + void **object); + + HRESULT (STDMETHODCALLTYPE *GetInstanceExtensions)( + ID3D12DXVKInteropDevice *This, + UINT *extension_count, + const char **extensions); + + HRESULT (STDMETHODCALLTYPE *GetDeviceExtensions)( + ID3D12DXVKInteropDevice *This, + UINT *extension_count, + const char **extensions); + + HRESULT (STDMETHODCALLTYPE *GetDeviceFeatures)( + ID3D12DXVKInteropDevice *This, + const VkPhysicalDeviceFeatures2 **features); + + HRESULT (STDMETHODCALLTYPE *GetVulkanHandles)( + ID3D12DXVKInteropDevice *This, + VkInstance *vk_instance, + VkPhysicalDevice *vk_physical_device, + VkDevice *vk_device); + + HRESULT (STDMETHODCALLTYPE *GetVulkanQueueInfo)( + ID3D12DXVKInteropDevice *This, + ID3D12CommandQueue *queue, + VkQueue *vk_queue, + UINT32 *vk_queue_family); + + void (STDMETHODCALLTYPE *GetVulkanImageLayout)( + ID3D12DXVKInteropDevice *This, + ID3D12Resource *resource, + D3D12_RESOURCE_STATES state, + VkImageLayout *vk_layout); + + HRESULT (STDMETHODCALLTYPE *GetVulkanResourceInfo)( + ID3D12DXVKInteropDevice *This, + ID3D12Resource *resource, + UINT64 *vk_handle, + UINT64 *buffer_offset); + + HRESULT (STDMETHODCALLTYPE *LockCommandQueue)( + ID3D12DXVKInteropDevice *This, + ID3D12CommandQueue *queue); + + HRESULT (STDMETHODCALLTYPE *UnlockCommandQueue)( + ID3D12DXVKInteropDevice *This, + ID3D12CommandQueue *queue); + + END_INTERFACE +} ID3D12DXVKInteropDeviceVtbl; + +interface ID3D12DXVKInteropDevice { + CONST_VTBL ID3D12DXVKInteropDeviceVtbl* lpVtbl; +}; +#endif