From b419b3dfbda5643aa80fa67af1387be2c30eef80 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 21 Feb 2018 01:04:28 +0100 Subject: [PATCH] [dxvk] Implemented typeless resolve This should allow a large number of Unity-based games to render at least a menu. --- src/dxvk/dxvk_context.cpp | 131 +++++++++++++++++++------------- src/dxvk/dxvk_context.h | 1 + src/dxvk/dxvk_meta_resolve.cpp | 134 +++++++++++++++++++++++++++++++++ src/dxvk/dxvk_meta_resolve.h | 50 ++++++++++++ src/dxvk/meson.build | 1 + 5 files changed, 263 insertions(+), 54 deletions(-) create mode 100644 src/dxvk/dxvk_meta_resolve.cpp create mode 100644 src/dxvk/dxvk_meta_resolve.h diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 05ae0f7f..742489b7 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -904,40 +904,42 @@ namespace dxvk { if (format == VK_FORMAT_UNDEFINED) format = srcImage->info().format; - VkImageSubresourceRange dstSubresourceRange = { - dstSubresources.aspectMask, - dstSubresources.mipLevel, 1, - dstSubresources.baseArrayLayer, - dstSubresources.layerCount }; - - VkImageSubresourceRange srcSubresourceRange = { - srcSubresources.aspectMask, - srcSubresources.mipLevel, 1, - srcSubresources.baseArrayLayer, - srcSubresources.layerCount }; - - // We only support resolving to the entire image - // area, so we might as well discard its contents - m_barriers.accessImage( - dstImage, dstSubresourceRange, - VK_IMAGE_LAYOUT_UNDEFINED, - dstImage->info().stages, - dstImage->info().access, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT); - m_barriers.accessImage( - srcImage, srcSubresourceRange, - srcImage->info().layout, - srcImage->info().stages, - srcImage->info().access, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_READ_BIT); - m_barriers.recordCommands(m_cmd); - - if (srcImage->info().format == format - && dstImage->info().format == format) { + if (dstImage->info().format == format + && srcImage->info().format == format) { + // Use the built-in Vulkan resolve function if the image + // formats both match the format of the resolve operation. + VkImageSubresourceRange dstSubresourceRange = { + dstSubresources.aspectMask, + dstSubresources.mipLevel, 1, + dstSubresources.baseArrayLayer, + dstSubresources.layerCount }; + + VkImageSubresourceRange srcSubresourceRange = { + srcSubresources.aspectMask, + srcSubresources.mipLevel, 1, + srcSubresources.baseArrayLayer, + srcSubresources.layerCount }; + + // We only support resolving to the entire image + // area, so we might as well discard its contents + m_barriers.accessImage( + dstImage, dstSubresourceRange, + VK_IMAGE_LAYOUT_UNDEFINED, + dstImage->info().stages, + dstImage->info().access, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT); + m_barriers.accessImage( + srcImage, srcSubresourceRange, + srcImage->info().layout, + srcImage->info().stages, + srcImage->info().access, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_READ_BIT); + m_barriers.recordCommands(m_cmd); + VkImageResolve imageRegion; imageRegion.srcSubresource = srcSubresources; imageRegion.srcOffset = VkOffset3D { 0, 0, 0 }; @@ -951,27 +953,48 @@ namespace dxvk { dstImage->handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageRegion); - } else { - // TODO implement - } - m_barriers.accessImage( - dstImage, dstSubresourceRange, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, - dstImage->info().layout, - dstImage->info().stages, - dstImage->info().access); - m_barriers.accessImage( - srcImage, srcSubresourceRange, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_READ_BIT, - srcImage->info().layout, - srcImage->info().stages, - srcImage->info().access); - m_barriers.recordCommands(m_cmd); + m_barriers.accessImage( + dstImage, dstSubresourceRange, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + dstImage->info().layout, + dstImage->info().stages, + dstImage->info().access); + m_barriers.accessImage( + srcImage, srcSubresourceRange, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + srcImage->info().layout, + srcImage->info().stages, + srcImage->info().access); + m_barriers.recordCommands(m_cmd); + } else { + // The trick here is to submit an empty render pass which + // performs the resolve op on properly typed image views. + const Rc fb = + new DxvkMetaResolveFramebuffer(m_device->vkd(), + dstImage, dstSubresources, + srcImage, srcSubresources, format); + + VkRenderPassBeginInfo info; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.pNext = nullptr; + info.renderPass = fb->renderPass(); + info.framebuffer = fb->framebuffer(); + info.renderArea = VkRect2D { { 0, 0 }, { + dstImage->info().extent.width, + dstImage->info().extent.height } }; + info.clearValueCount = 0; + info.pClearValues = nullptr; + + m_cmd->cmdBeginRenderPass(&info, VK_SUBPASS_CONTENTS_INLINE); + m_cmd->cmdEndRenderPass(); + + m_cmd->trackResource(fb); + } } diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 8627aaee..4c01ae06 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -6,6 +6,7 @@ #include "dxvk_context_state.h" #include "dxvk_data.h" #include "dxvk_event.h" +#include "dxvk_meta_resolve.h" #include "dxvk_query.h" #include "dxvk_query_pool.h" #include "dxvk_util.h" diff --git a/src/dxvk/dxvk_meta_resolve.cpp b/src/dxvk/dxvk_meta_resolve.cpp new file mode 100644 index 00000000..991cb6ab --- /dev/null +++ b/src/dxvk/dxvk_meta_resolve.cpp @@ -0,0 +1,134 @@ +#include "dxvk_meta_resolve.h" + +namespace dxvk { + + DxvkMetaResolveFramebuffer::DxvkMetaResolveFramebuffer( + const Rc& vkd, + const Rc& dstImage, + VkImageSubresourceLayers dstLayers, + const Rc& srcImage, + VkImageSubresourceLayers srcLayers, + VkFormat format) + : m_vkd(vkd) { + // Create a render pass with one render + // target and one resolve attachment. + std::array attachmentInfos = {{ + VkAttachmentDescription { + 0, format, dstImage->info().sampleCount, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_STORE, + VK_IMAGE_LAYOUT_UNDEFINED, + dstImage->info().layout }, + VkAttachmentDescription { + 0, format, srcImage->info().sampleCount, + VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_STORE_OP_STORE, + srcImage->info().layout, + srcImage->info().layout }, + }}; + + // Make sure layout transitions are correctly ordered + std::array subpassDeps = {{ + { VK_SUBPASS_EXTERNAL, 0, + srcImage->info().stages | dstImage->info().stages, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + srcImage->info().access | dstImage->info().access, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0 }, + { 0, VK_SUBPASS_EXTERNAL, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + srcImage->info().stages | dstImage->info().stages, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + srcImage->info().access | dstImage->info().access, 0 }, + }}; + + VkAttachmentReference dstAttachmentRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + VkAttachmentReference srcAttachmentRef = { 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + + VkSubpassDescription spInfo; + spInfo.flags = 0; + spInfo.inputAttachmentCount = 0; + spInfo.pInputAttachments = nullptr; + spInfo.colorAttachmentCount = 1; + spInfo.pColorAttachments = &srcAttachmentRef; + spInfo.pResolveAttachments = &dstAttachmentRef; + spInfo.pDepthStencilAttachment = nullptr; + spInfo.preserveAttachmentCount = 0; + spInfo.pPreserveAttachments = nullptr; + + VkRenderPassCreateInfo rpInfo; + rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rpInfo.pNext = nullptr; + rpInfo.flags = 0; + rpInfo.attachmentCount = attachmentInfos.size(); + rpInfo.pAttachments = attachmentInfos.data(); + rpInfo.subpassCount = 1; + rpInfo.pSubpasses = &spInfo; + rpInfo.dependencyCount = subpassDeps.size(); + rpInfo.pDependencies = subpassDeps.data(); + + m_vkd->vkCreateRenderPass(m_vkd->device(), &rpInfo, nullptr, &m_renderPass); + + // Create views for the destination and source image + VkImageViewCreateInfo dstInfo; + dstInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + dstInfo.pNext = nullptr; + dstInfo.flags = 0; + dstInfo.image = dstImage->handle(); + dstInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + dstInfo.format = format; + dstInfo.components = VkComponentMapping { + VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; + dstInfo.subresourceRange = VkImageSubresourceRange { + dstLayers.aspectMask, dstLayers.mipLevel, 1, + dstLayers.baseArrayLayer, dstLayers.layerCount }; + + VkImageViewCreateInfo srcInfo; + srcInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + srcInfo.pNext = nullptr; + srcInfo.flags = 0; + srcInfo.image = srcImage->handle(); + srcInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + srcInfo.format = format; + srcInfo.components = VkComponentMapping { + VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; + srcInfo.subresourceRange = VkImageSubresourceRange { + srcLayers.aspectMask, srcLayers.mipLevel, 1, + srcLayers.baseArrayLayer, srcLayers.layerCount }; + + m_vkd->vkCreateImageView(m_vkd->device(), &dstInfo, nullptr, &m_dstImageView); + m_vkd->vkCreateImageView(m_vkd->device(), &srcInfo, nullptr, &m_srcImageView); + + // Create a framebuffer containing the two image views + std::array attachments = {{ m_dstImageView, m_srcImageView }}; + + VkFramebufferCreateInfo fboInfo; + fboInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fboInfo.pNext = nullptr; + fboInfo.flags = 0; + fboInfo.renderPass = m_renderPass; + fboInfo.attachmentCount = attachments.size(); + fboInfo.pAttachments = attachments.data(); + fboInfo.width = dstImage->info().extent.width; + fboInfo.height = dstImage->info().extent.height; + fboInfo.layers = dstLayers.layerCount; + + m_vkd->vkCreateFramebuffer(m_vkd->device(), &fboInfo, nullptr, &m_framebuffer); + } + + + DxvkMetaResolveFramebuffer::~DxvkMetaResolveFramebuffer() { + m_vkd->vkDestroyFramebuffer(m_vkd->device(), m_framebuffer, nullptr); + m_vkd->vkDestroyImageView (m_vkd->device(), m_srcImageView, nullptr); + m_vkd->vkDestroyImageView (m_vkd->device(), m_dstImageView, nullptr); + m_vkd->vkDestroyRenderPass (m_vkd->device(), m_renderPass, nullptr); + } + +} diff --git a/src/dxvk/dxvk_meta_resolve.h b/src/dxvk/dxvk_meta_resolve.h new file mode 100644 index 00000000..fdd56750 --- /dev/null +++ b/src/dxvk/dxvk_meta_resolve.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include "dxvk_barrier.h" +#include "dxvk_cmdlist.h" +#include "dxvk_resource.h" + +namespace dxvk { + + /** + * \brief Meta resolve framebuffer + * + * Stores a framebuffer and image view objects + * for a meta resolve operation. Can be tracked. + */ + class DxvkMetaResolveFramebuffer : public DxvkResource { + + public: + + DxvkMetaResolveFramebuffer( + const Rc& vkd, + const Rc& dstImage, + VkImageSubresourceLayers dstLayers, + const Rc& srcImage, + VkImageSubresourceLayers srcLayers, + VkFormat format); + + ~DxvkMetaResolveFramebuffer(); + + VkRenderPass renderPass() const { + return m_renderPass; + } + + VkFramebuffer framebuffer() const { + return m_framebuffer; + } + + private: + + const Rc m_vkd; + + VkRenderPass m_renderPass; + VkImageView m_dstImageView; + VkImageView m_srcImageView; + VkFramebuffer m_framebuffer; + + }; + +} diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 37b8824a..e1f58a1c 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -26,6 +26,7 @@ dxvk_src = files([ 'dxvk_lifetime.cpp', 'dxvk_main.cpp', 'dxvk_memory.cpp', + 'dxvk_meta_resolve.cpp', 'dxvk_options.cpp', 'dxvk_pipecache.cpp', 'dxvk_pipelayout.cpp',