From 95740eb78c40d4a4fb8a2b5882e696f729fb6f51 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 6 Mar 2021 02:33:39 +0100 Subject: [PATCH] [dxvk] Use clears to impelemnt image copies if possible Optimizes away a large amount of redundant depth-stencil clear+copy madness in Final Fantasy XIV for a small performance improvement. --- src/dxvk/dxvk_context.cpp | 71 +++++++++++++++++++++++++++++++++++++++ src/dxvk/dxvk_context.h | 8 +++++ src/vulkan/vulkan_util.h | 9 +++++ 3 files changed, 88 insertions(+) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 67d3c826c..72da2a7d3 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -766,6 +766,10 @@ namespace dxvk { VkOffset3D srcOffset, VkExtent3D extent) { this->spillRenderPass(true); + + if (this->copyImageClear(dstImage, dstSubresource, dstOffset, extent, srcImage, srcSubresource)) + return; + this->prepareImage(m_execBarriers, dstImage, vk::makeSubresourceRange(dstSubresource)); this->prepareImage(m_execBarriers, srcImage, vk::makeSubresourceRange(srcSubresource)); @@ -3183,6 +3187,73 @@ namespace dxvk { } + bool DxvkContext::copyImageClear( + const Rc& dstImage, + VkImageSubresourceLayers dstSubresource, + VkOffset3D dstOffset, + VkExtent3D dstExtent, + const Rc& srcImage, + VkImageSubresourceLayers srcSubresource) { + // If the source image has a pending deferred clear, we can + // implement the copy by clearing the destination image to + // the same clear value. + const VkImageUsageFlags attachmentUsage + = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT + | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + + if (!(dstImage->info().usage & attachmentUsage) + || !(srcImage->info().usage & attachmentUsage)) + return false; + + // Ignore 3D images since those are complicated to handle + if (dstImage->info().type == VK_IMAGE_TYPE_3D + || srcImage->info().type == VK_IMAGE_TYPE_3D) + return false; + + // Find a pending clear that overlaps with the source image + const DxvkDeferredClear* clear = nullptr; + + for (const auto& entry : m_deferredClears) { + // Entries in the deferred clear array cannot overlap, so + // if we find an entry covering all source subresources, + // it's the only one in the list that does. + if ((entry.imageView->image() == srcImage) && ((srcSubresource.aspectMask & entry.clearAspects) == srcSubresource.aspectMask) + && (vk::checkSubresourceRangeSuperset(entry.imageView->subresources(), vk::makeSubresourceRange(srcSubresource)))) { + clear = &entry; + break; + } + } + + if (!clear) + return false; + + // Create a view for the destination image with the general + // properties ofthe source image view used for the clear + DxvkImageViewCreateInfo viewInfo = clear->imageView->info(); + viewInfo.type = dstImage->info().type == VK_IMAGE_TYPE_1D + ? VK_IMAGE_VIEW_TYPE_1D_ARRAY + : VK_IMAGE_VIEW_TYPE_2D_ARRAY; + viewInfo.minLevel = dstSubresource.mipLevel; + viewInfo.numLevels = 1; + viewInfo.minLayer = dstSubresource.baseArrayLayer; + viewInfo.numLayers = dstSubresource.layerCount; + + // That is, if the formats are actually compatible + // so that we can safely use the same clear value + if (!dstImage->isViewCompatible(viewInfo.format)) + return false; + + // Ignore mismatched size for now, needs more testing since we'd + // need to prepare the image first and then call clearImageViewFb + if (dstImage->mipLevelExtent(dstSubresource.mipLevel) != dstExtent) + return false; + + auto view = m_device->createImageView(dstImage, viewInfo); + this->deferClear(view, clear->clearAspects, clear->clearValue); + return true; + } + + void DxvkContext::resolveImageHw( const Rc& dstImage, const Rc& srcImage, diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 36c37112f..4b5231f36 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -1067,6 +1067,14 @@ namespace dxvk { VkOffset3D srcOffset, VkExtent3D extent); + bool copyImageClear( + const Rc& dstImage, + VkImageSubresourceLayers dstSubresource, + VkOffset3D dstOffset, + VkExtent3D dstExtent, + const Rc& srcImage, + VkImageSubresourceLayers srcSubresource); + void resolveImageHw( const Rc& dstImage, const Rc& srcImage, diff --git a/src/vulkan/vulkan_util.h b/src/vulkan/vulkan_util.h index e8e318047..a2899d3ef 100644 --- a/src/vulkan/vulkan_util.h +++ b/src/vulkan/vulkan_util.h @@ -67,6 +67,15 @@ namespace dxvk::vk { && a.baseArrayLayer + a.layerCount > b.baseArrayLayer; } + inline bool checkSubresourceRangeSuperset( + const VkImageSubresourceRange& a, + const VkImageSubresourceRange& b) { + return a.baseMipLevel <= b.baseMipLevel + && a.baseMipLevel + a.levelCount >= b.baseMipLevel + b.levelCount + && a.baseArrayLayer <= b.baseArrayLayer + && a.baseArrayLayer + a.layerCount >= b.baseArrayLayer + b.layerCount; + } + }