diff --git a/meson.build b/meson.build index f044c8f4..36b22d39 100644 --- a/meson.build +++ b/meson.build @@ -122,7 +122,7 @@ glsl_generator = generator(glsl_compiler, ) dxvk_version = vcs_tag( - command: ['git', 'describe', '--dirty=+'], + command: ['git', 'describe', '--dirty=-async'], input: 'version.h.in', output: 'version.h', ) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 6486d267..cc59297e 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -4540,7 +4540,8 @@ namespace dxvk { : DxvkContextFlag::GpDirtyRasterizerState); // Retrieve and bind actual Vulkan pipeline handle - auto pipelineInfo = m_state.gp.pipeline->getPipelineHandle(m_state.gp.state); + auto pipelineInfo = m_state.gp.pipeline->getPipelineHandle( + m_state.gp.state, this->checkAsyncCompilationCompat()); if (unlikely(!pipelineInfo.first)) return false; @@ -4890,7 +4891,7 @@ namespace dxvk { } - void DxvkContext::updateFramebuffer() { + void DxvkContext::updateFramebuffer(bool isDraw) { if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer)) { m_flags.clr(DxvkContextFlag::GpDirtyFramebuffer); @@ -4914,6 +4915,11 @@ namespace dxvk { m_state.gp.state.omSwizzle[i] = DxvkOmAttachmentSwizzle(mapping); } + if (isDraw) { + for (uint32_t i = 0; i < fbInfo.numAttachments(); i++) + fbInfo.getAttachment(i).view->setRtBindingFrameId(m_device->getCurrentFrameId()); + } + m_flags.set(DxvkContextFlag::GpDirtyPipelineState); } } @@ -5363,7 +5369,7 @@ namespace dxvk { } if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer)) - this->updateFramebuffer(); + this->updateFramebuffer(true); if (!m_flags.test(DxvkContextFlag::GpRenderPassBound)) this->startRenderPass(); @@ -5774,6 +5780,14 @@ namespace dxvk { return true; } + bool DxvkContext::checkAsyncCompilationCompat() { + bool fbCompat = true; + for (uint32_t i = 0; fbCompat && i < m_state.om.framebufferInfo.numAttachments(); i++) { + const auto& attachment = m_state.om.framebufferInfo.getAttachment(i); + fbCompat &= attachment.view->getRtBindingAsyncCompilationCompat(); + } + return fbCompat; + } DxvkGraphicsPipeline* DxvkContext::lookupGraphicsPipeline( const DxvkGraphicsPipelineShaders& shaders) { diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index fe24c556..0930bd81 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -1493,7 +1493,7 @@ namespace dxvk { DxvkFramebufferInfo makeFramebufferInfo( const DxvkRenderTargets& renderTargets); - void updateFramebuffer(); + void updateFramebuffer(bool isDraw = false); void applyRenderTargetLoadLayouts(); @@ -1576,6 +1576,8 @@ namespace dxvk { const Rc& buffer, VkDeviceSize copySize); + bool checkAsyncCompilationCompat(); + DxvkGraphicsPipeline* lookupGraphicsPipeline( const DxvkGraphicsPipelineShaders& shaders); diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index c0ecbbb4..f4e4fd4f 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -899,7 +899,8 @@ namespace dxvk { std::pair DxvkGraphicsPipeline::getPipelineHandle( - const DxvkGraphicsPipelineStateInfo& state) { + const DxvkGraphicsPipelineStateInfo& state, + bool async) { DxvkGraphicsPipelineInstance* instance = this->findInstance(state); if (unlikely(!instance)) { @@ -907,28 +908,36 @@ namespace dxvk { if (!this->validatePipelineState(state, true)) return std::make_pair(VK_NULL_HANDLE, DxvkGraphicsPipelineType::FastPipeline); + bool useAsync = m_device->config().enableAsync && async; + // Prevent other threads from adding new instances and check again - std::unique_lock lock(m_mutex); + std::unique_lock lock(useAsync ? m_asyncMutex : m_mutex); instance = this->findInstance(state); if (!instance) { - // Keep pipeline object locked, at worst we're going to stall - // a state cache worker and the current thread needs priority. - bool canCreateBasePipeline = this->canCreateBasePipeline(state); - instance = this->createInstance(state, canCreateBasePipeline); - - // Unlock here since we may dispatch the pipeline to a worker, - // which will then acquire it to increment the use counter. - lock.unlock(); - - // If necessary, compile an optimized pipeline variant - if (!instance->fastHandle.load()) + if (useAsync) { + lock.unlock(); m_workers->compileGraphicsPipeline(this, state); - - // Only store pipelines in the state cache that cannot benefit - // from pipeline libraries, or if that feature is disabled. - if (!canCreateBasePipeline) - this->writePipelineStateToCache(state); + return std::make_pair(VK_NULL_HANDLE, DxvkGraphicsPipelineType::FastPipeline); + } else { + // Keep pipeline object locked, at worst we're going to stall + // a state cache worker and the current thread needs priority. + bool canCreateBasePipeline = this->canCreateBasePipeline(state); + instance = this->createInstance(state, canCreateBasePipeline); + + // Unlock here since we may dispatch the pipeline to a worker, + // which will then acquire it to increment the use counter. + lock.unlock(); + + // If necessary, compile an optimized pipeline variant + if (!instance->fastHandle.load()) + m_workers->compileGraphicsPipeline(this, state); + + // Only store pipelines in the state cache that cannot benefit + // from pipeline libraries, or if that feature is disabled. + if (!canCreateBasePipeline) + this->writePipelineStateToCache(state); + } } } diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index cfc424cd..437c73a9 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -521,10 +521,12 @@ namespace dxvk { * Retrieves a pipeline handle for the given pipeline * state. If necessary, a new pipeline will be created. * \param [in] state Pipeline state vector + * \param [in] async Compile asynchronously * \returns Pipeline handle and handle type */ std::pair getPipelineHandle( - const DxvkGraphicsPipelineStateInfo& state); + const DxvkGraphicsPipelineStateInfo& state, + bool async); /** * \brief Compiles a pipeline @@ -576,6 +578,8 @@ namespace dxvk { alignas(CACHE_LINE_SIZE) dxvk::mutex m_mutex; + alignas(CACHE_LINE_SIZE) + dxvk::mutex m_asyncMutex; sync::List m_pipelines; uint32_t m_useCount = 0; diff --git a/src/dxvk/dxvk_image.h b/src/dxvk/dxvk_image.h index 285c5e9e..989a240f 100644 --- a/src/dxvk/dxvk_image.h +++ b/src/dxvk/dxvk_image.h @@ -547,6 +547,37 @@ namespace dxvk { view->imageSubresources()); } + /** + * \brief Sets render target usage frame number + * + * The image view will track internally when + * it was last used as a render target. This + * info is used for async shader compilation. + * \param [in] frameId Frame number + */ + void setRtBindingFrameId(uint32_t frameId) { + if (frameId != m_rtBindingFrameId) { + if (frameId == m_rtBindingFrameId + 1) + m_rtBindingFrameCount += 1; + else + m_rtBindingFrameCount = 0; + + m_rtBindingFrameId = frameId; + } + } + + /** + * \brief Checks for async pipeline compatibility + * + * Asynchronous pipeline compilation may be enabled if the + * render target has been drawn to in the previous frames. + * \param [in] frameId Current frame ID + * \returns \c true if async compilation is supported + */ + bool getRtBindingAsyncCompilationCompat() const { + return m_rtBindingFrameCount >= 5; + } + private: Rc m_vkd; @@ -555,6 +586,9 @@ namespace dxvk { DxvkImageViewCreateInfo m_info; VkImageView m_views[ViewCount]; + uint32_t m_rtBindingFrameId = 0; + uint32_t m_rtBindingFrameCount = 0; + void createView(VkImageViewType type, uint32_t numLayers); }; diff --git a/src/dxvk/dxvk_options.cpp b/src/dxvk/dxvk_options.cpp index 00f284aa..f13429eb 100644 --- a/src/dxvk/dxvk_options.cpp +++ b/src/dxvk/dxvk_options.cpp @@ -10,6 +10,14 @@ namespace dxvk { trackPipelineLifetime = config.getOption("dxvk.trackPipelineLifetime", Tristate::Auto); useRawSsbo = config.getOption("dxvk.useRawSsbo", Tristate::Auto); hud = config.getOption("dxvk.hud", ""); + + if (env::getEnvVar("DXVK_ASYNC") == "1") + enableAsync = true; + else + enableAsync = config.getOption("dxvk.enableAsync", false); + + if (enableAsync) + enableGraphicsPipelineLibrary = Tristate::False; } } diff --git a/src/dxvk/dxvk_options.h b/src/dxvk/dxvk_options.h index da5bf406..05a7823e 100644 --- a/src/dxvk/dxvk_options.h +++ b/src/dxvk/dxvk_options.h @@ -2,6 +2,8 @@ #include "../util/config/config.h" +#include "dxvk_include.h" + namespace dxvk { struct DxvkOptions { @@ -24,6 +26,9 @@ namespace dxvk { /// Enables pipeline lifetime tracking Tristate trackPipelineLifetime; + // Enable async pipelines + bool enableAsync; + /// Shader-related options Tristate useRawSsbo;