From 02aa1736f56c8b36316008a026922f50537428cd Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 8 Jul 2022 18:00:08 +0200 Subject: [PATCH] [dxvk] Try to create cached optimized pipeline whenever possible --- src/dxvk/dxvk_device.cpp | 10 ++++++ src/dxvk/dxvk_device.h | 6 ++++ src/dxvk/dxvk_graphics.cpp | 72 ++++++++++++++++++++++++++------------ src/dxvk/dxvk_graphics.h | 3 +- 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index 76fed23ed..fab902065 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -51,6 +51,16 @@ namespace dxvk { } + bool DxvkDevice::canUsePipelineCacheControl() const { + // Don't bother with this unless the device also supports shader module + // identifiers, since decoding and hashing the shaders is slow otherwise + // and likely provides no benefit over linking pipeline libraries. + return m_features.extPipelineCreationCacheControl.pipelineCreationCacheControl + && m_features.extShaderModuleIdentifier.shaderModuleIdentifier + && m_options.enableGraphicsPipelineLibrary != Tristate::True; + } + + DxvkFramebufferSize DxvkDevice::getDefaultFramebufferSize() const { return DxvkFramebufferSize { m_properties.core.properties.limits.maxFramebufferWidth, diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 29c7a1f28..3cf743a65 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -202,6 +202,12 @@ namespace dxvk { */ bool canUseGraphicsPipelineLibrary() const; + /** + * \brief Checks whether pipeline creation cache control can be used + * \returns \c true if all required features are supported. + */ + bool canUsePipelineCacheControl() const; + /** * \brief Queries default framebuffer size * \returns Default framebuffer size diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index 743c8a1ae..a8a00da30 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -589,7 +589,7 @@ namespace dxvk { || instance->isCompiling.exchange(VK_TRUE, std::memory_order_acquire)) return; - VkPipeline pipeline = this->createOptimizedPipeline(state); + VkPipeline pipeline = this->createOptimizedPipeline(state, 0); instance->fastHandle.store(pipeline, std::memory_order_release); } @@ -600,18 +600,29 @@ namespace dxvk { VkPipeline fastHandle = VK_NULL_HANDLE; if (this->canCreateBasePipeline(state)) { - DxvkGraphicsPipelineVertexInputState viState(m_device, state); - DxvkGraphicsPipelineFragmentOutputState foState(m_device, state, m_shaders.fs.ptr()); + // Try to create an optimized pipeline from the cache + // first, since this is expected to be the fastest path. + if (m_device->canUsePipelineCacheControl()) { + fastHandle = this->createOptimizedPipeline(state, + VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT); + } - DxvkGraphicsPipelineBaseInstanceKey key; - key.viLibrary = m_manager->createVertexInputLibrary(viState); - key.foLibrary = m_manager->createFragmentOutputLibrary(foState); - key.args.depthClipEnable = state.rs.depthClipEnable(); + if (!fastHandle) { + // If that didn't succeed, link a pipeline using the + // pre-compiled fragment and vertex shader libraries. + DxvkGraphicsPipelineVertexInputState viState(m_device, state); + DxvkGraphicsPipelineFragmentOutputState foState(m_device, state, m_shaders.fs.ptr()); - baseHandle = this->createBaseInstance(key)->handle; + DxvkGraphicsPipelineBaseInstanceKey key; + key.viLibrary = m_manager->createVertexInputLibrary(viState); + key.foLibrary = m_manager->createFragmentOutputLibrary(foState); + key.args.depthClipEnable = state.rs.depthClipEnable(); + + baseHandle = this->createBaseInstance(key)->handle; + } } else { // Create optimized variant right away, no choice - fastHandle = this->createOptimizedPipeline(state); + fastHandle = this->createOptimizedPipeline(state, 0); } m_stats->numGraphicsPipelines += 1; @@ -727,7 +738,8 @@ namespace dxvk { VkPipeline DxvkGraphicsPipeline::createOptimizedPipeline( - const DxvkGraphicsPipelineStateInfo& state) const { + const DxvkGraphicsPipelineStateInfo& state, + VkPipelineCreateFlags flags) const { auto vk = m_device->vkd(); if (Logger::logLevel() <= LogLevel::Debug) { @@ -772,16 +784,24 @@ namespace dxvk { // Build stage infos for all provided shaders DxvkShaderStageInfo stageInfo(m_device); - stageInfo.addStage(VK_SHADER_STAGE_VERTEX_BIT, getShaderCode(m_shaders.vs, state), &specInfo); - if (m_shaders.tcs != nullptr) - stageInfo.addStage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, getShaderCode(m_shaders.tcs, state), &specInfo); - if (m_shaders.tes != nullptr) - stageInfo.addStage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, getShaderCode(m_shaders.tes, state), &specInfo); - if (m_shaders.gs != nullptr) - stageInfo.addStage(VK_SHADER_STAGE_GEOMETRY_BIT, getShaderCode(m_shaders.gs, state), &specInfo); - if (m_shaders.fs != nullptr) - stageInfo.addStage(VK_SHADER_STAGE_FRAGMENT_BIT, getShaderCode(m_shaders.fs, state), &specInfo); + if (flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT) { + stageInfo.addStage(VK_SHADER_STAGE_VERTEX_BIT, m_vsLibrary->getModuleIdentifier(), &specInfo); + + if (m_shaders.fs != nullptr) + stageInfo.addStage(VK_SHADER_STAGE_FRAGMENT_BIT, m_fsLibrary->getModuleIdentifier(), &specInfo); + } else { + stageInfo.addStage(VK_SHADER_STAGE_VERTEX_BIT, getShaderCode(m_shaders.vs, state), &specInfo); + + if (m_shaders.tcs != nullptr) + stageInfo.addStage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, getShaderCode(m_shaders.tcs, state), &specInfo); + if (m_shaders.tes != nullptr) + stageInfo.addStage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, getShaderCode(m_shaders.tes, state), &specInfo); + if (m_shaders.gs != nullptr) + stageInfo.addStage(VK_SHADER_STAGE_GEOMETRY_BIT, getShaderCode(m_shaders.gs, state), &specInfo); + if (m_shaders.fs != nullptr) + stageInfo.addStage(VK_SHADER_STAGE_FRAGMENT_BIT, getShaderCode(m_shaders.fs, state), &specInfo); + } DxvkGraphicsPipelineVertexInputState viState(m_device, state); DxvkGraphicsPipelinePreRasterizationState prState(m_device, state, m_shaders.gs.ptr()); @@ -793,6 +813,7 @@ namespace dxvk { dyInfo.pDynamicStates = dynamicStates.data(); VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &foState.rtInfo }; + info.flags = flags; info.stageCount = stageInfo.getStageCount(); info.pStages = stageInfo.getStageInfos(); info.pVertexInputState = &viState.viInfo; @@ -811,9 +832,16 @@ namespace dxvk { info.pTessellationState = nullptr; VkPipeline pipeline = VK_NULL_HANDLE; - if (vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline) != VK_SUCCESS) { - Logger::err("DxvkGraphicsPipeline: Failed to compile pipeline"); - this->logPipelineState(LogLevel::Error, state); + VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline); + + if (vr != VK_SUCCESS) { + // Ignore any error if we're trying to create a cached pipeline. If linking or + // compiling an optimized pipeline fail later, we'll still be printing errors. + if (!(flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT)) { + Logger::err(str::format("DxvkGraphicsPipeline: Failed to compile pipeline: ", vr)); + this->logPipelineState(LogLevel::Error, state); + } + return VK_NULL_HANDLE; } diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index d0abc0604..29e316d6a 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -425,7 +425,8 @@ namespace dxvk { const DxvkGraphicsPipelineBaseInstanceKey& key) const; VkPipeline createOptimizedPipeline( - const DxvkGraphicsPipelineStateInfo& state) const; + const DxvkGraphicsPipelineStateInfo& state, + VkPipelineCreateFlags flags) const; void destroyPipeline( VkPipeline pipeline) const;