From 5d5be87402f35ae7a1afd3e6219b7a4c5cd923df Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 30 Apr 2018 17:04:13 +0200 Subject: [PATCH 1/5] [dxvk] Oprimize render target clear operations We can save one image layout transition when clearing a render target by delaying clears until vkCmdBeginRenderPass is called. --- src/dxvk/dxvk_context.cpp | 75 ++++++++++++++++++++--------------- src/dxvk/dxvk_context.h | 4 +- src/dxvk/dxvk_context_state.h | 9 +++-- src/dxvk/dxvk_framebuffer.cpp | 16 ++++---- 4 files changed, 59 insertions(+), 45 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index e6ccc7838..9e0f58cb5 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -29,7 +29,8 @@ namespace dxvk { // undefined, so we have to bind and set up everything // before any draw or dispatch command is recorded. m_flags.clr( - DxvkContextFlag::GpRenderPassBound); + DxvkContextFlag::GpRenderPassBound, + DxvkContextFlag::GpClearRenderTargets); m_flags.set( DxvkContextFlag::GpDirtyPipeline, @@ -88,7 +89,9 @@ namespace dxvk { void DxvkContext::bindRenderTargets(const DxvkRenderTargets& targets) { m_state.om.renderTargets = targets; - // TODO execute pending clears + // If necessary, perform clears on the active render targets + if (m_flags.test(DxvkContextFlag::GpClearRenderTargets)) + this->startRenderPass(); // Set up default render pass ops this->resetRenderPassOps( @@ -398,8 +401,8 @@ namespace dxvk { this->spillRenderPass(); DxvkAttachmentOps op; - op.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - op.loadLayout = imageView->imageInfo().layout; + op.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + op.loadLayout = VK_IMAGE_LAYOUT_UNDEFINED; op.storeOp = VK_ATTACHMENT_STORE_OP_STORE; op.storeLayout = imageView->imageInfo().layout; @@ -418,29 +421,33 @@ namespace dxvk { } this->renderPassBindFramebuffer( - m_device->createFramebuffer(attachments), ops); - } else { - // Make sure that the currently bound - // framebuffer can be rendered to - this->startRenderPass(); - } - - // Clear the attachment in quesion - VkClearAttachment clearInfo; - clearInfo.aspectMask = clearAspects; - clearInfo.colorAttachment = attachmentIndex; - clearInfo.clearValue = clearValue; - - if (attachmentIndex < 0) - clearInfo.colorAttachment = 0; - - m_cmd->cmdClearAttachments( - 1, &clearInfo, 1, &clearRect); - - // If we used a temporary framebuffer, we'll have to unbind it - // again in order to not disturb subsequent rendering commands. - if (attachmentIndex < 0) + m_device->createFramebuffer(attachments), + ops, 1, &clearValue); this->renderPassUnbindFramebuffer(); + } else if (m_flags.test(DxvkContextFlag::GpRenderPassBound)) { + // Clear the attachment in quesion. For color images, + // the attachment index for the current subpass is + // equal to the render pass attachment index. + VkClearAttachment clearInfo; + clearInfo.aspectMask = clearAspects; + clearInfo.colorAttachment = attachmentIndex; + clearInfo.clearValue = clearValue; + + m_cmd->cmdClearAttachments( + 1, &clearInfo, 1, &clearRect); + } else { + // Perform the clear when starting the render pass + if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT) { + m_state.om.renderPassOps.colorOps[attachmentIndex].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + m_state.om.renderPassOps.colorOps[attachmentIndex].loadLayout = VK_IMAGE_LAYOUT_UNDEFINED; + } else { + m_state.om.renderPassOps.depthOps.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + m_state.om.renderPassOps.depthOps.loadLayout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + m_state.om.clearValues[attachmentIndex] = clearValue; + m_flags.set(DxvkContextFlag::GpClearRenderTargets); + } } @@ -1515,10 +1522,13 @@ namespace dxvk { if (!m_flags.test(DxvkContextFlag::GpRenderPassBound) && (m_state.om.framebuffer != nullptr)) { m_flags.set(DxvkContextFlag::GpRenderPassBound); + m_flags.clr(DxvkContextFlag::GpClearRenderTargets); this->renderPassBindFramebuffer( m_state.om.framebuffer, - m_state.om.renderPassOps); + m_state.om.renderPassOps, + m_state.om.clearValues.size(), + m_state.om.clearValues.data()); // Don't discard image contents if we have // to spill the current render pass @@ -1530,7 +1540,8 @@ namespace dxvk { void DxvkContext::spillRenderPass() { - // TODO execute pending clears + if (m_flags.test(DxvkContextFlag::GpClearRenderTargets)) + this->startRenderPass(); if (m_flags.test(DxvkContextFlag::GpRenderPassBound)) { m_flags.clr(DxvkContextFlag::GpRenderPassBound); @@ -1541,7 +1552,9 @@ namespace dxvk { void DxvkContext::renderPassBindFramebuffer( const Rc& framebuffer, - const DxvkRenderPassOps& ops) { + const DxvkRenderPassOps& ops, + uint32_t clearValueCount, + const VkClearValue* clearValues) { const DxvkFramebufferSize fbSize = framebuffer->size(); VkRect2D renderArea; @@ -1554,8 +1567,8 @@ namespace dxvk { info.renderPass = framebuffer->getRenderPassHandle(ops); info.framebuffer = framebuffer->handle(); info.renderArea = renderArea; - info.clearValueCount = 0; - info.pClearValues = nullptr; + info.clearValueCount = clearValueCount; + info.pClearValues = clearValues; m_cmd->cmdBeginRenderPass(&info, VK_SUBPASS_CONTENTS_INLINE); diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index e3743fa76..7528f935a 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -644,7 +644,9 @@ namespace dxvk { void renderPassBindFramebuffer( const Rc& framebuffer, - const DxvkRenderPassOps& ops); + const DxvkRenderPassOps& ops, + uint32_t clearValueCount, + const VkClearValue* clearValues); void renderPassUnbindFramebuffer(); diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h index 5516e362c..9d7677cbd 100644 --- a/src/dxvk/dxvk_context_state.h +++ b/src/dxvk/dxvk_context_state.h @@ -16,12 +16,13 @@ namespace dxvk { /** * \brief Graphics pipeline state flags * - * Stores some information on which state of the - * graphics pipeline has changed and/or needs to - * be updated. + * Stores some information on which state + * of the graphics and compute pipelines + * has changed and/or needs to be updated. */ enum class DxvkContextFlag : uint64_t { GpRenderPassBound, ///< Render pass is currently bound + GpClearRenderTargets, ///< Render targets need to be cleared GpDirtyFramebuffer, ///< Framebuffer binding is out of date GpDirtyPipeline, ///< Graphics pipeline binding is out of date GpDirtyPipelineState, ///< Graphics pipeline needs to be recompiled @@ -54,7 +55,7 @@ namespace dxvk { struct DxvkOutputMergerState { - std::array clearValue; + std::array clearValues = { }; DxvkRenderTargets renderTargets; DxvkRenderPassOps renderPassOps; diff --git a/src/dxvk/dxvk_framebuffer.cpp b/src/dxvk/dxvk_framebuffer.cpp index 468656628..77d0ddd64 100644 --- a/src/dxvk/dxvk_framebuffer.cpp +++ b/src/dxvk/dxvk_framebuffer.cpp @@ -13,20 +13,18 @@ namespace dxvk { m_renderSize (computeRenderSize(defaultSize)) { std::array views; - uint32_t viewId = 0; - for (uint32_t i = 0; i < MaxNumRenderTargets; i++) { if (m_renderTargets.color[i].view != nullptr) { - views[viewId] = m_renderTargets.color[i].view->handle(); - m_attachments[viewId] = &m_renderTargets.color[i]; - viewId += 1; + views[m_attachmentCount] = m_renderTargets.color[i].view->handle(); + m_attachments[m_attachmentCount] = &m_renderTargets.color[i]; + m_attachmentCount += 1; } } if (m_renderTargets.depth.view != nullptr) { - views[viewId] = m_renderTargets.depth.view->handle(); - m_attachments[viewId] = &m_renderTargets.depth; - viewId += 1; + views[m_attachmentCount] = m_renderTargets.depth.view->handle(); + m_attachments[m_attachmentCount] = &m_renderTargets.depth; + m_attachmentCount += 1; } VkFramebufferCreateInfo info; @@ -34,7 +32,7 @@ namespace dxvk { info.pNext = nullptr; info.flags = 0; info.renderPass = m_renderPass->getDefaultHandle(); - info.attachmentCount = viewId; + info.attachmentCount = m_attachmentCount; info.pAttachments = views.data(); info.width = m_renderSize.width; info.height = m_renderSize.height; From 370ff34e9fa173bc3129f03d33d25459ce6dddee Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 30 Apr 2018 19:36:42 +0200 Subject: [PATCH 2/5] [d3d11] Fix sampler state validation for anisotropy Fixes an issue where samplers are not created in Path of Exile. --- src/d3d11/d3d11_sampler.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/d3d11/d3d11_sampler.cpp b/src/d3d11/d3d11_sampler.cpp index dacfcd061..f16541ff7 100644 --- a/src/d3d11/d3d11_sampler.cpp +++ b/src/d3d11/d3d11_sampler.cpp @@ -87,14 +87,10 @@ namespace dxvk { return E_INVALIDARG; } - if (filterBits & 0x40 /* anisotropic */) { - if (pDesc->MaxAnisotropy < 1 - || pDesc->MaxAnisotropy > 16) - return E_INVALIDARG; - } else if (pDesc->MaxAnisotropy < 0 - || pDesc->MaxAnisotropy > 16) { - return E_INVALIDARG; - } else { + if (pDesc->MaxAnisotropy < 0 + || pDesc->MaxAnisotropy > 16) { + return E_INVALIDARG; + } else if ((filterBits & 0x40) == 0 /* not anisotropic */) { // Reset anisotropy if it is not used pDesc->MaxAnisotropy = 0; } From cf1358b2f42345a2880a24aa5ff51bb9e50791f6 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 30 Apr 2018 21:42:16 +0200 Subject: [PATCH 3/5] [dxvk] Fixed partial depth-stencil clear operations --- src/dxvk/dxvk_context.cpp | 73 ++++++++++++++++++++++++------------ src/dxvk/dxvk_renderpass.cpp | 14 ++++--- src/dxvk/dxvk_renderpass.h | 24 ++++++++++-- 3 files changed, 78 insertions(+), 33 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 9e0f58cb5..1a2bfcd56 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -389,6 +389,35 @@ namespace dxvk { const VkClearValue& clearValue) { this->updateFramebuffer(); + // Prepare attachment ops + DxvkColorAttachmentOps colorOp; + colorOp.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + colorOp.loadLayout = imageView->imageInfo().layout; + colorOp.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorOp.storeLayout = imageView->imageInfo().layout; + + DxvkDepthAttachmentOps depthOp; + depthOp.loadOpD = VK_ATTACHMENT_LOAD_OP_LOAD; + depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_LOAD; + depthOp.loadLayout = imageView->imageInfo().layout; + depthOp.storeOpD = VK_ATTACHMENT_STORE_OP_STORE; + depthOp.storeOpS = VK_ATTACHMENT_STORE_OP_STORE; + depthOp.storeLayout = imageView->imageInfo().layout; + + if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT) + colorOp.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + + if (clearAspects & VK_IMAGE_ASPECT_DEPTH_BIT) + depthOp.loadOpD = VK_ATTACHMENT_LOAD_OP_CLEAR; + + if (clearAspects & VK_IMAGE_ASPECT_STENCIL_BIT) + depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_CLEAR; + + if (clearAspects == imageView->info().aspect) { + colorOp.loadLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthOp.loadLayout = VK_IMAGE_LAYOUT_UNDEFINED; + } + // Check whether the render target view is an attachment // of the current framebuffer. If not, we need to create // a temporary framebuffer. @@ -400,12 +429,6 @@ namespace dxvk { if (attachmentIndex < 0) { this->spillRenderPass(); - DxvkAttachmentOps op; - op.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - op.loadLayout = VK_IMAGE_LAYOUT_UNDEFINED; - op.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - op.storeLayout = imageView->imageInfo().layout; - // Set up and bind a temporary framebuffer DxvkRenderTargets attachments; DxvkRenderPassOps ops; @@ -413,11 +436,11 @@ namespace dxvk { if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT) { attachments.color[0].view = imageView; attachments.color[0].layout = imageView->pickLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - ops.colorOps[0] = op; + ops.colorOps[0] = colorOp; } else { attachments.depth.view = imageView; attachments.depth.layout = imageView->pickLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - ops.depthOps = op; + ops.depthOps = depthOp; } this->renderPassBindFramebuffer( @@ -437,13 +460,11 @@ namespace dxvk { 1, &clearInfo, 1, &clearRect); } else { // Perform the clear when starting the render pass - if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT) { - m_state.om.renderPassOps.colorOps[attachmentIndex].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - m_state.om.renderPassOps.colorOps[attachmentIndex].loadLayout = VK_IMAGE_LAYOUT_UNDEFINED; - } else { - m_state.om.renderPassOps.depthOps.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - m_state.om.renderPassOps.depthOps.loadLayout = VK_IMAGE_LAYOUT_UNDEFINED; - } + if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT) + m_state.om.renderPassOps.colorOps[attachmentIndex] = colorOp; + + if (clearAspects & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) + m_state.om.renderPassOps.depthOps = depthOp; m_state.om.clearValues[attachmentIndex] = clearValue; m_flags.set(DxvkContextFlag::GpClearRenderTargets); @@ -1586,17 +1607,23 @@ namespace dxvk { const DxvkRenderTargets& renderTargets, DxvkRenderPassOps& renderPassOps) { renderPassOps.depthOps = renderTargets.depth.view != nullptr - ? DxvkAttachmentOps { - VK_ATTACHMENT_LOAD_OP_LOAD, renderTargets.depth.view->imageInfo().layout, - VK_ATTACHMENT_STORE_OP_STORE, renderTargets.depth.view->imageInfo().layout } - : DxvkAttachmentOps { }; + ? DxvkDepthAttachmentOps { + VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_LOAD_OP_LOAD, + renderTargets.depth.view->imageInfo().layout, + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_STORE_OP_STORE, + renderTargets.depth.view->imageInfo().layout } + : DxvkDepthAttachmentOps { }; for (uint32_t i = 0; i < MaxNumRenderTargets; i++) { renderPassOps.colorOps[i] = renderTargets.color[i].view != nullptr - ? DxvkAttachmentOps { - VK_ATTACHMENT_LOAD_OP_LOAD, renderTargets.color[i].view->imageInfo().layout, - VK_ATTACHMENT_STORE_OP_STORE, renderTargets.color[i].view->imageInfo().layout } - : DxvkAttachmentOps { }; + ? DxvkColorAttachmentOps { + VK_ATTACHMENT_LOAD_OP_LOAD, + renderTargets.color[i].view->imageInfo().layout, + VK_ATTACHMENT_STORE_OP_STORE, + renderTargets.color[i].view->imageInfo().layout } + : DxvkColorAttachmentOps { }; } // TODO provide a sane alternative for this diff --git a/src/dxvk/dxvk_renderpass.cpp b/src/dxvk/dxvk_renderpass.cpp index e24109c4c..343cddaf8 100644 --- a/src/dxvk/dxvk_renderpass.cpp +++ b/src/dxvk/dxvk_renderpass.cpp @@ -89,10 +89,10 @@ namespace dxvk { desc.flags = 0; desc.format = m_format.depth.format; desc.samples = m_format.sampleCount; - desc.loadOp = ops.depthOps.loadOp; - desc.storeOp = ops.depthOps.storeOp; - desc.stencilLoadOp = ops.depthOps.loadOp; - desc.stencilStoreOp = ops.depthOps.storeOp; + desc.loadOp = ops.depthOps.loadOpD; + desc.storeOp = ops.depthOps.storeOpD; + desc.stencilLoadOp = ops.depthOps.loadOpS; + desc.stencilStoreOp = ops.depthOps.storeOpS; desc.initialLayout = ops.depthOps.loadLayout; desc.finalLayout = ops.depthOps.storeLayout; @@ -179,9 +179,11 @@ namespace dxvk { bool DxvkRenderPass::compareOps( const DxvkRenderPassOps& a, const DxvkRenderPassOps& b) { - bool eq = a.depthOps.loadOp == b.depthOps.loadOp + bool eq = a.depthOps.loadOpD == b.depthOps.loadOpD + && a.depthOps.loadOpS == b.depthOps.loadOpS && a.depthOps.loadLayout == b.depthOps.loadLayout - && a.depthOps.storeOp == b.depthOps.storeOp + && a.depthOps.storeOpD == b.depthOps.storeOpD + && a.depthOps.storeOpS == b.depthOps.storeOpS && a.depthOps.storeLayout == b.depthOps.storeLayout; for (uint32_t i = 0; i < MaxNumRenderTargets && eq; i++) { diff --git a/src/dxvk/dxvk_renderpass.h b/src/dxvk/dxvk_renderpass.h index a65c52535..d44aa487e 100644 --- a/src/dxvk/dxvk_renderpass.h +++ b/src/dxvk/dxvk_renderpass.h @@ -35,12 +35,12 @@ namespace dxvk { /** - * \brief Attachment transitions + * \brief Color attachment transitions * * Stores the load/store ops and the initial * and final layout of a single attachment. */ - struct DxvkAttachmentOps { + struct DxvkColorAttachmentOps { VkAttachmentLoadOp loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; VkImageLayout loadLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkAttachmentStoreOp storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -48,6 +48,22 @@ namespace dxvk { }; + /** + * \brief Depth attachment transitions + * + * Stores the load/store ops and the initial and + * final layout of the depth-stencil attachment. + */ + struct DxvkDepthAttachmentOps { + VkAttachmentLoadOp loadOpD = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + VkAttachmentLoadOp loadOpS = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + VkImageLayout loadLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkAttachmentStoreOp storeOpD = VK_ATTACHMENT_STORE_OP_STORE; + VkAttachmentStoreOp storeOpS = VK_ATTACHMENT_STORE_OP_STORE; + VkImageLayout storeLayout = VK_IMAGE_LAYOUT_GENERAL; + }; + + /** * \brief Render pass transitions * @@ -56,8 +72,8 @@ namespace dxvk { * from a group of render passes with the same format. */ struct DxvkRenderPassOps { - DxvkAttachmentOps depthOps; - DxvkAttachmentOps colorOps[MaxNumRenderTargets]; + DxvkDepthAttachmentOps depthOps; + DxvkColorAttachmentOps colorOps[MaxNumRenderTargets]; }; From d201a1f7c67f2736371b0a23fe4b35a8e1908bfb Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 1 May 2018 16:45:28 +0200 Subject: [PATCH 4/5] [dxvk] Made pipe manager and pipeline classes thread-safe --- src/dxvk/dxvk_compute.cpp | 49 ++++++++++++++++++++++++++++------- src/dxvk/dxvk_compute.h | 7 +++++ src/dxvk/dxvk_graphics.cpp | 49 ++++++++++++++++++++++++++++------- src/dxvk/dxvk_graphics.h | 7 ++++- src/dxvk/dxvk_pipemanager.cpp | 20 +++++++++----- src/dxvk/dxvk_pipemanager.h | 3 +++ 6 files changed, 108 insertions(+), 27 deletions(-) diff --git a/src/dxvk/dxvk_compute.cpp b/src/dxvk/dxvk_compute.cpp index 5ff7962c1..80f5ac9a1 100644 --- a/src/dxvk/dxvk_compute.cpp +++ b/src/dxvk/dxvk_compute.cpp @@ -42,19 +42,50 @@ namespace dxvk { VkPipeline DxvkComputePipeline::getPipelineHandle( const DxvkComputePipelineStateInfo& state, DxvkStatCounters& stats) { - for (const PipelineStruct& pair : m_pipelines) { - if (pair.stateVector == state) - return pair.pipeline; + VkPipeline pipeline = VK_NULL_HANDLE; + + { std::lock_guard lock(m_mutex); + + if (this->findPipeline(state, pipeline)) + return pipeline; } - VkPipeline pipeline = this->compilePipeline(state, m_basePipeline); - m_pipelines.push_back({ state, pipeline }); + // If no pipeline exists with the given state vector, + // create a new one and add it to the pipeline set. + VkPipeline newPipeline = this->compilePipeline(state, m_basePipeline); - if (m_basePipeline == VK_NULL_HANDLE) - m_basePipeline = pipeline; + { std::lock_guard lock(m_mutex); + + // Discard the pipeline if another thread + // was faster compiling the same pipeline + if (this->findPipeline(state, pipeline)) { + m_vkd->vkDestroyPipeline(m_vkd->device(), newPipeline, nullptr); + return pipeline; + } + + // Add new pipeline to the set + m_pipelines.push_back({ state, newPipeline }); + + if (m_basePipeline == VK_NULL_HANDLE) + m_basePipeline = newPipeline; + + stats.addCtr(DxvkStatCounter::PipeCountCompute, 1); + return newPipeline; + } + } + + + bool DxvkComputePipeline::findPipeline( + const DxvkComputePipelineStateInfo& state, + VkPipeline& pipeline) const { + for (const PipelineStruct& pair : m_pipelines) { + if (pair.stateVector == state) { + pipeline = pair.pipeline; + return true; + } + } - stats.addCtr(DxvkStatCounter::PipeCountCompute, 1); - return pipeline; + return false; } diff --git a/src/dxvk/dxvk_compute.h b/src/dxvk/dxvk_compute.h index 335589d2f..9a2101d31 100644 --- a/src/dxvk/dxvk_compute.h +++ b/src/dxvk/dxvk_compute.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "dxvk_binding.h" #include "dxvk_pipecache.h" #include "dxvk_pipelayout.h" @@ -76,10 +78,15 @@ namespace dxvk { Rc m_layout; Rc m_cs; + sync::Spinlock m_mutex; std::vector m_pipelines; VkPipeline m_basePipeline = VK_NULL_HANDLE; + bool findPipeline( + const DxvkComputePipelineStateInfo& state, + VkPipeline& pipeline) const; + VkPipeline compilePipeline( const DxvkComputePipelineStateInfo& state, VkPipeline baseHandle) const; diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index 554434d4a..32c8db247 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -78,23 +78,52 @@ namespace dxvk { VkPipeline DxvkGraphicsPipeline::getPipelineHandle( const DxvkGraphicsPipelineStateInfo& state, DxvkStatCounters& stats) { + VkPipeline pipeline = VK_NULL_HANDLE; - for (const PipelineStruct& pair : m_pipelines) { - if (pair.stateVector == state) - return pair.pipeline; + { std::lock_guard lock(m_mutex); + + if (this->findPipeline(state, pipeline)) + return pipeline; } - VkPipeline pipeline = this->validatePipelineState(state) + // If no pipeline exists with the given state vector, + // create a new one and add it to the pipeline set. + VkPipeline newPipeline = this->validatePipelineState(state) ? this->compilePipeline(state, m_basePipeline) : VK_NULL_HANDLE; - m_pipelines.push_back({ state, pipeline }); + { std::lock_guard lock(m_mutex); + + // Discard the pipeline if another thread + // was faster compiling the same pipeline + if (this->findPipeline(state, pipeline)) { + m_vkd->vkDestroyPipeline(m_vkd->device(), newPipeline, nullptr); + return pipeline; + } + + // Add new pipeline to the set + m_pipelines.push_back({ state, newPipeline }); + + if (m_basePipeline == VK_NULL_HANDLE) + m_basePipeline = newPipeline; + + stats.addCtr(DxvkStatCounter::PipeCountGraphics, 1); + return newPipeline; + } + } + + + bool DxvkGraphicsPipeline::findPipeline( + const DxvkGraphicsPipelineStateInfo& state, + VkPipeline& pipeline) const { + for (const PipelineStruct& pair : m_pipelines) { + if (pair.stateVector == state) { + pipeline = pair.pipeline; + return true; + } + } - if (m_basePipeline == VK_NULL_HANDLE) - m_basePipeline = pipeline; - - stats.addCtr(DxvkStatCounter::PipeCountGraphics, 1); - return pipeline; + return false; } diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index 8b6f56a4e..5c40928e6 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "dxvk_binding.h" #include "dxvk_constant_state.h" @@ -160,10 +160,15 @@ namespace dxvk { DxvkGraphicsCommonPipelineStateInfo m_common; + sync::Spinlock m_mutex; std::vector m_pipelines; VkPipeline m_basePipeline = VK_NULL_HANDLE; + bool findPipeline( + const DxvkGraphicsPipelineStateInfo& state, + VkPipeline& pipeline) const; + VkPipeline compilePipeline( const DxvkGraphicsPipelineStateInfo& state, VkPipeline baseHandle) const; diff --git a/src/dxvk/dxvk_pipemanager.cpp b/src/dxvk/dxvk_pipemanager.cpp index 7b9f554f6..d827b4db2 100644 --- a/src/dxvk/dxvk_pipemanager.cpp +++ b/src/dxvk/dxvk_pipemanager.cpp @@ -21,17 +21,19 @@ namespace dxvk { } - bool DxvkPipelineKeyEq::operator () (const DxvkComputePipelineKey& a, const DxvkComputePipelineKey& b) const { + bool DxvkPipelineKeyEq::operator () ( + const DxvkComputePipelineKey& a, + const DxvkComputePipelineKey& b) const { return a.cs == b.cs; } - bool DxvkPipelineKeyEq::operator () (const DxvkGraphicsPipelineKey& a, const DxvkGraphicsPipelineKey& b) const { - return a.vs == b.vs - && a.tcs == b.tcs - && a.tes == b.tes - && a.gs == b.gs - && a.fs == b.fs; + bool DxvkPipelineKeyEq::operator () ( + const DxvkGraphicsPipelineKey& a, + const DxvkGraphicsPipelineKey& b) const { + return a.vs == b.vs && a.tcs == b.tcs + && a.tes == b.tes && a.gs == b.gs + && a.fs == b.fs; } @@ -52,6 +54,8 @@ namespace dxvk { if (cs == nullptr) return nullptr; + std::lock_guard lock(m_mutex); + DxvkComputePipelineKey key; key.cs = cs; @@ -77,6 +81,8 @@ namespace dxvk { if (vs == nullptr) return nullptr; + std::lock_guard lock(m_mutex); + DxvkGraphicsPipelineKey key; key.vs = vs; key.tcs = tcs; diff --git a/src/dxvk/dxvk_pipemanager.h b/src/dxvk/dxvk_pipemanager.h index 3690bbd20..2400cdc9c 100644 --- a/src/dxvk/dxvk_pipemanager.h +++ b/src/dxvk/dxvk_pipemanager.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "dxvk_compute.h" @@ -99,6 +100,8 @@ namespace dxvk { const DxvkDevice* m_device; + std::mutex m_mutex; + std::unordered_map< DxvkComputePipelineKey, Rc, From 5e02c1bb2fc4ff05c2a596f97f1c0560de50c06a Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 1 May 2018 16:56:33 +0200 Subject: [PATCH 5/5] [dxvk] Move DxvkPipelineManager instance back to DxvkDevice --- src/dxvk/dxvk_context.cpp | 3 ++- src/dxvk/dxvk_context.h | 1 + src/dxvk/dxvk_device.cpp | 2 ++ src/dxvk/dxvk_device.h | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 1a2bfcd56..e8a4a3bc3 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -9,10 +9,11 @@ namespace dxvk { DxvkContext::DxvkContext( const Rc& device, const Rc& pipelineCache, + const Rc& pipelineManager, const Rc& metaClearObjects) : m_device (device), m_pipeCache (pipelineCache), - m_pipeMgr (new DxvkPipelineManager(device.ptr())), + m_pipeMgr (pipelineManager), m_metaClear (metaClearObjects) { } diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 7528f935a..e10ddfe7d 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -30,6 +30,7 @@ namespace dxvk { DxvkContext( const Rc& device, const Rc& pipelineCache, + const Rc& pipelineManager, const Rc& metaClearObjects); ~DxvkContext(); diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index 72aaa4b2a..d92e779d6 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -16,6 +16,7 @@ namespace dxvk { m_memory (new DxvkMemoryAllocator (adapter, vkd)), m_renderPassPool (new DxvkRenderPassPool (vkd)), m_pipelineCache (new DxvkPipelineCache (vkd)), + m_pipelineManager (new DxvkPipelineManager (this)), m_metaClearObjects(new DxvkMetaClearObjects (vkd)), m_unboundResources(this), m_submissionQueue (this) { @@ -106,6 +107,7 @@ namespace dxvk { Rc DxvkDevice::createContext() { return new DxvkContext(this, m_pipelineCache, + m_pipelineManager, m_metaClearObjects); } diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index dc54ae000..6c91de48a 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -355,6 +355,7 @@ namespace dxvk { Rc m_memory; Rc m_renderPassPool; Rc m_pipelineCache; + Rc m_pipelineManager; Rc m_metaClearObjects; DxvkUnboundResources m_unboundResources;