diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index 96bdb050..98f5b2ed 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -63,22 +63,25 @@ namespace dxvk { VkPipeline DxvkGraphicsPipeline::getPipelineHandle( const DxvkGraphicsPipelineStateInfo& state, const DxvkRenderPass* renderPass) { - DxvkGraphicsPipelineInstance* instance = nullptr; + DxvkGraphicsPipelineInstance* instance = this->findInstance(state, renderPass); - { std::lock_guard lock(m_mutex); - + if (unlikely(!instance)) { + // Exit early if the state vector is invalid + if (!this->validatePipelineState(state)) + return VK_NULL_HANDLE; + + // Prevent other threads from adding new instances and check again + std::lock_guard lock(m_mutex); instance = this->findInstance(state, renderPass); - - if (instance) - return instance->pipeline(); - - instance = this->createInstance(state, renderPass); - } - - if (!instance) - return VK_NULL_HANDLE; - this->writePipelineStateToCache(state, renderPass->format()); + if (!instance) { + // Keep pipeline object locked, at worst we're going to stall + // a state cache worker and the current thread needs priority. + instance = this->createInstance(state, renderPass); + this->writePipelineStateToCache(state, renderPass->format()); + } + } + return instance->pipeline(); } @@ -86,7 +89,13 @@ namespace dxvk { void DxvkGraphicsPipeline::compilePipeline( const DxvkGraphicsPipelineStateInfo& state, const DxvkRenderPass* renderPass) { - std::lock_guard lock(m_mutex); + // Exit early if the state vector is invalid + if (!this->validatePipelineState(state)) + return; + + // Keep the object locked while compiling a pipeline since compiling + // similar pipelines concurrently is fragile on some drivers + std::lock_guard lock(m_mutex); if (!this->findInstance(state, renderPass)) this->createInstance(state, renderPass); @@ -96,15 +105,10 @@ namespace dxvk { DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::createInstance( const DxvkGraphicsPipelineStateInfo& state, const DxvkRenderPass* renderPass) { - // If the pipeline state vector is invalid, don't try - // to create a new pipeline, it won't work anyway. - if (!this->validatePipelineState(state)) - return nullptr; - - VkPipeline newPipelineHandle = this->createPipeline(state, renderPass); + VkPipeline pipeline = this->createPipeline(state, renderPass); m_pipeMgr->m_numGraphicsPipelines += 1; - return &m_pipelines.emplace_back(state, renderPass, newPipelineHandle); + return &(*m_pipelines.emplace(state, renderPass, pipeline)); } diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index 4194599d..3a4d93f3 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -2,6 +2,8 @@ #include +#include "../util/sync/sync_list.h" + #include "dxvk_bind_mask.h" #include "dxvk_constant_state.h" #include "dxvk_graphics_state.h" @@ -80,7 +82,7 @@ namespace dxvk { DxvkGraphicsPipelineInstance() : m_stateVector (), - m_renderPass (VK_NULL_HANDLE), + m_renderPass (nullptr), m_pipeline (VK_NULL_HANDLE) { } DxvkGraphicsPipelineInstance( @@ -220,8 +222,9 @@ namespace dxvk { DxvkGraphicsCommonPipelineStateInfo m_common; // List of pipeline instances, shared between threads - alignas(CACHE_LINE_SIZE) sync::Spinlock m_mutex; - std::vector m_pipelines; + alignas(CACHE_LINE_SIZE) + dxvk::mutex m_mutex; + sync::List m_pipelines; DxvkGraphicsPipelineInstance* createInstance( const DxvkGraphicsPipelineStateInfo& state,