wineopenxr: Support acquiring/releasing images from D3D12 swapchains.

This commit is contained in:
Yuxuan Shui 2024-05-09 22:35:09 +01:00 committed by Arkadiusz Hiler
parent ef4785f325
commit 08c566d928
2 changed files with 221 additions and 11 deletions

View file

@ -914,6 +914,7 @@ XrResult WINAPI wine_xrDestroyInstance(XrInstance instance)
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);
}
@ -1055,6 +1056,11 @@ XrResult WINAPI wine_xrCreateSession(XrInstance instance, const XrSessionCreateI
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;
@ -1102,6 +1108,16 @@ XrResult WINAPI wine_xrCreateSession(XrInstance instance, const XrSessionCreateI
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;
@ -1723,6 +1739,7 @@ XrResult WINAPI wine_xrCreateSwapchain(XrSession session, const XrSwapchainCreat
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)
@ -1731,6 +1748,12 @@ static void release_d3d12_resources(wine_XrSwapchain *wine_swapchain, uint32_t i
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)
@ -1783,6 +1806,46 @@ 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;
@ -1857,6 +1920,16 @@ XrResult WINAPI wine_xrEnumerateSwapchainImages(XrSwapchain swapchain, uint32_t
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);
@ -1902,7 +1975,28 @@ XrResult WINAPI wine_xrEnumerateSwapchainImages(XrSwapchain swapchain, uint32_t
}
device_ext->lpVtbl->Release(device_ext);
wine_swapchain->images = (XrSwapchainImageBaseHeader *)our_d3d12;
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);
@ -2106,53 +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);
if (wine_session->session_type == SESSION_TYPE_D3D12)
return XR_ERROR_RUNTIME_FAILURE;
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 (wine_session->session_type == SESSION_TYPE_D3D12)
return XR_ERROR_RUNTIME_FAILURE;
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;
}

View file

@ -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;
@ -23,8 +39,13 @@ typedef struct wine_XrInstance {
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;
@ -56,9 +77,14 @@ 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 {