diff options
author | Stelios Tsampas | 2019-05-13 19:46:53 +0300 |
---|---|---|
committer | Stelios Tsampas | 2019-05-13 19:46:53 +0300 |
commit | f47f1a9195cf87d6e1d62a4f64a90770ca6aad15 (patch) | |
tree | 2916c67a1f20672f15a54cef9ce6f3b62cf41f50 | |
parent | 76fe43955669b65c7214aa5e9089d9a865cecc97 (diff) | |
download | aur-f47f1a9195cf87d6e1d62a4f64a90770ca6aad15.tar.gz |
version 0.11
-rw-r--r-- | .SRCINFO | 8 | ||||
-rw-r--r-- | PKGBUILD | 12 | ||||
-rw-r--r-- | dxvk-async.patch | 634 |
3 files changed, 649 insertions, 5 deletions
@@ -1,6 +1,6 @@ pkgbase = d9vk-mingw - pkgdesc = A d3d9 to vk layer based off DXVK's codebase. Mingw version - pkgver = 0.10 + pkgdesc = A d3d9 to vk layer based off DXVK's codebase, mingw version + pkgver = 0.11 pkgrel = 1 url = https://github.com/Joshua-Ashton/d9vk arch = x86_64 @@ -17,12 +17,14 @@ pkgbase = d9vk-mingw depends = bash provides = d9vk conflicts = d9vk - source = git+https://github.com/Joshua-Ashton/d9vk.git#tag=0.10 + source = git+https://github.com/Joshua-Ashton/d9vk.git#tag=0.11 source = setup_d9vk source = extraopts.patch + source = dxvk-async.patch sha256sums = SKIP sha256sums = 7147644664ef33d04f7b18683c47be95b5664c57cf6d63fdc019d915deebd37a sha256sums = d73f948fd39da218141cc72c7373f59e6fc289630e155b6e51d18597455d0040 + sha256sums = 6c6936b753903ba59ee9e2b6c8fc533bf60cba894cf4288ec0239c35b86796cd pkgname = d9vk-mingw @@ -1,9 +1,9 @@ # Maintainer: loathingkernel <loathingkernel @at gmail .dot com> pkgname=d9vk-mingw -pkgver=0.10 +pkgver=0.11 pkgrel=1 -pkgdesc="A d3d9 to vk layer based off DXVK's codebase. Mingw version" +pkgdesc="A d3d9 to vk layer based off DXVK's codebase, mingw version" arch=('x86_64') url="https://github.com/Joshua-Ashton/d9vk" license=('zlib/libpng') @@ -15,15 +15,19 @@ source=( "git+https://github.com/Joshua-Ashton/d9vk.git#tag=$pkgver" "setup_d9vk" "extraopts.patch" + "dxvk-async.patch" ) sha256sums=( "SKIP" "7147644664ef33d04f7b18683c47be95b5664c57cf6d63fdc019d915deebd37a" "d73f948fd39da218141cc72c7373f59e6fc289630e155b6e51d18597455d0040" + "6c6936b753903ba59ee9e2b6c8fc533bf60cba894cf4288ec0239c35b86796cd" ) prepare() { cd d9vk + # Patch crossfiles with extra optimizations from makepkg.conf + # If building fails, comment the line below to disable them. patch -p1 -i ../extraopts.patch CFLAGS="$CPPFLAGS $CFLAGS" LDFLAGS="${LDFLAGS/,-z,relro,-z,now/}" @@ -33,6 +37,10 @@ prepare() { sed -i build-win32.txt \ -e "s|@CARGS@|\'${CFLAGS// /\',\'}\'|g" \ -e "s|@LDARGS@|\'${LDFLAGS// /\',\'}\'|g" + # Uncomment to enable dxvk async patch. + # Enable at your own risk, if you don't know what it is, + # leave it as is. You have been warned. + #patch -p1 -i ../dxvk-async.patch } build() { diff --git a/dxvk-async.patch b/dxvk-async.patch new file mode 100644 index 000000000000..f7f4f10aa28c --- /dev/null +++ b/dxvk-async.patch @@ -0,0 +1,634 @@ +diff --git a/meson.build b/meson.build +index d2d1b6cd..aff9446c 100644 +--- a/meson.build ++++ b/meson.build +@@ -90,7 +90,7 @@ else + endif + + 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 9f9a4c25..b9e34316 100644 +--- a/src/dxvk/dxvk_context.cpp ++++ b/src/dxvk/dxvk_context.cpp +@@ -590,7 +590,7 @@ namespace dxvk { + const Rc<DxvkImageView>& imageView, + VkImageAspectFlags clearAspects, + const VkClearValue& clearValue) { +- this->updateFramebuffer(); ++ this->updateFramebuffer(false); + + // Prepare attachment ops + DxvkColorAttachmentOps colorOp; +@@ -2237,7 +2237,7 @@ namespace dxvk { + VkOffset3D offset, + VkExtent3D extent, + VkClearValue value) { +- this->updateFramebuffer(); ++ this->updateFramebuffer(false); + + // Find out if the render target view is currently bound, + // so that we can avoid spilling the render pass if it is. +@@ -3354,7 +3354,7 @@ namespace dxvk { + // Retrieve and bind actual Vulkan pipeline handle + m_gpActivePipeline = m_state.gp.pipeline != nullptr && m_state.om.framebuffer != nullptr + ? m_state.gp.pipeline->getPipelineHandle(m_state.gp.state, +- m_state.om.framebuffer->getRenderPass()) ++ m_state.om.framebuffer->getRenderPass(), this->checkAsyncCompilationCompat()) + : VK_NULL_HANDLE; + + if (m_gpActivePipeline != VK_NULL_HANDLE) { +@@ -3623,7 +3623,7 @@ namespace dxvk { + } + + +- void DxvkContext::updateFramebuffer() { ++ void DxvkContext::updateFramebuffer(bool isDraw) { + if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer)) { + m_flags.clr(DxvkContextFlag::GpDirtyFramebuffer); + +@@ -3642,6 +3642,11 @@ namespace dxvk { + : VkComponentMapping(); + } + ++ if (isDraw) { ++ for (uint32_t i = 0; i < fb->numAttachments(); i++) ++ fb->getAttachment(i).view->setRtBindingFrameId(m_device->getCurrentFrameId()); ++ } ++ + m_flags.set(DxvkContextFlag::GpDirtyPipelineState); + } + } +@@ -3876,7 +3881,7 @@ namespace dxvk { + + void DxvkContext::commitGraphicsState(bool indexed) { + if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer)) +- this->updateFramebuffer(); ++ this->updateFramebuffer(true); + + if (!m_flags.test(DxvkContextFlag::GpRenderPassBound)) + this->startRenderPass(); +@@ -4124,4 +4129,13 @@ namespace dxvk { + } + } + ++ ++ bool DxvkContext::checkAsyncCompilationCompat() { ++ bool fbCompat = true; ++ for (uint32_t i = 0; fbCompat && i < m_state.om.framebuffer->numAttachments(); i++) { ++ const auto& attachment = m_state.om.framebuffer->getAttachment(i); ++ fbCompat &= attachment.view->getRtBindingAsyncCompilationCompat(); ++ } ++ return fbCompat; ++ } + } +\ No newline at end of file +diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h +index 68aa5542..b264f6ee 100644 +--- a/src/dxvk/dxvk_context.h ++++ b/src/dxvk/dxvk_context.h +@@ -1076,7 +1076,7 @@ namespace dxvk { + VkDescriptorSet set, + const DxvkPipelineLayout* layout); + +- void updateFramebuffer(); ++ void updateFramebuffer(bool isDraw); + + void updateIndexBufferBinding(); + void updateVertexBufferBindings(); +@@ -1102,6 +1102,8 @@ namespace dxvk { + + void commitGraphicsPostBarriers(); + ++ bool checkAsyncCompilationCompat(); ++ + void emitMemoryBarrier( + VkPipelineStageFlags srcStages, + VkAccessFlags srcAccess, +diff --git a/src/dxvk/dxvk_framebuffer.h b/src/dxvk/dxvk_framebuffer.h +index 49133845..ff80297b 100644 +--- a/src/dxvk/dxvk_framebuffer.h ++++ b/src/dxvk/dxvk_framebuffer.h +@@ -120,8 +120,8 @@ namespace dxvk { + * \brief Retrieves render pass + * \returns Render pass reference + */ +- const DxvkRenderPass& getRenderPass() const { +- return *m_renderPass; ++ const Rc<DxvkRenderPass>& getRenderPass() const { ++ return m_renderPass; + } + + /** +@@ -221,4 +221,4 @@ namespace dxvk { + + }; + +-} +\ No newline at end of file ++} +diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp +index d50fb209..3e2132c0 100644 +--- a/src/dxvk/dxvk_graphics.cpp ++++ b/src/dxvk/dxvk_graphics.cpp +@@ -4,6 +4,7 @@ + #include "dxvk_device.h" + #include "dxvk_graphics.h" + #include "dxvk_pipemanager.h" ++#include "dxvk_pipecompiler.h" + #include "dxvk_spec_const.h" + #include "dxvk_state_cache.h" + +@@ -79,8 +80,6 @@ namespace dxvk { + + + DxvkGraphicsPipeline::~DxvkGraphicsPipeline() { +- for (const auto& instance : m_pipelines) +- this->destroyPipeline(instance.pipeline()); + } + + +@@ -100,8 +99,9 @@ namespace dxvk { + + VkPipeline DxvkGraphicsPipeline::getPipelineHandle( + const DxvkGraphicsPipelineStateInfo& state, +- const DxvkRenderPass& renderPass) { +- VkRenderPass renderPassHandle = renderPass.getDefaultHandle(); ++ const Rc<DxvkRenderPass>& renderPass, ++ bool async) { ++ VkRenderPass renderPassHandle = renderPass->getDefaultHandle(); + + VkPipeline newPipelineHandle = VK_NULL_HANDLE; + +@@ -119,29 +119,49 @@ namespace dxvk { + + // If no pipeline instance exists with the given state + // vector, create a new one and add it to the list. +- newPipelineHandle = this->compilePipeline(state, renderPass, m_basePipeline); ++ Rc<DxvkGraphicsPipelineInstance> newPipeline = ++ new DxvkGraphicsPipelineInstance(m_pipeMgr->m_device->vkd(), ++ state, renderPassHandle, VK_NULL_HANDLE); ++ ++ if (async && m_pipeMgr->m_compiler != nullptr) ++ m_pipeMgr->m_compiler->queueCompilation(this, newPipeline, renderPass); ++ else ++ newPipelineHandle = this->compileInstance(newPipeline, renderPass); + + // Add new pipeline to the set +- m_pipelines.emplace_back(state, renderPassHandle, newPipelineHandle); ++ m_pipelines.push_back(newPipeline); + m_pipeMgr->m_numGraphicsPipelines += 1; +- +- if (!m_basePipeline && newPipelineHandle) +- m_basePipeline = newPipelineHandle; + } + +- if (newPipelineHandle != VK_NULL_HANDLE) +- this->writePipelineStateToCache(state, renderPass.format()); +- + return newPipelineHandle; + } + + +- const DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::findInstance( ++ VkPipeline DxvkGraphicsPipeline::compileInstance( ++ const Rc<DxvkGraphicsPipelineInstance>& instance, ++ const Rc<DxvkRenderPass>& renderPass) { ++ VkPipeline newPipelineHandle = this->compilePipeline( ++ instance->m_stateVector, *renderPass, m_basePipeline); ++ ++ if (!instance->setPipeline(newPipelineHandle)) { ++ m_vkd->vkDestroyPipeline(m_vkd->device(), newPipelineHandle, nullptr); ++ } else { ++ if (!m_basePipeline && newPipelineHandle) ++ m_basePipeline = newPipelineHandle; ++ if (newPipelineHandle != VK_NULL_HANDLE) ++ this->writePipelineStateToCache(instance->m_stateVector, renderPass->format()); ++ } ++ ++ return newPipelineHandle; ++ } ++ ++ ++ DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::findInstance( + const DxvkGraphicsPipelineStateInfo& state, + VkRenderPass renderPass) const { + for (const auto& instance : m_pipelines) { +- if (instance.isCompatible(state, renderPass)) +- return &instance; ++ if (instance->isCompatible(state, renderPass)) ++ return instance.ptr(); + } + + return nullptr; +diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h +index e5686950..8811f10c 100644 +--- a/src/dxvk/dxvk_graphics.h ++++ b/src/dxvk/dxvk_graphics.h +@@ -132,19 +132,25 @@ namespace dxvk { + * Stores a state vector and the + * corresponding pipeline handle. + */ +- class DxvkGraphicsPipelineInstance { +- ++ class DxvkGraphicsPipelineInstance: public RcObject { ++ friend class DxvkGraphicsPipeline; + public: + + DxvkGraphicsPipelineInstance() { } + DxvkGraphicsPipelineInstance( ++ const Rc<vk::DeviceFn>& vkd, + const DxvkGraphicsPipelineStateInfo& state, + VkRenderPass rp, + VkPipeline pipe) +- : m_stateVector (state), ++ : m_vkd (vkd), ++ m_stateVector (state), + m_renderPass (rp), + m_pipeline (pipe) { } + ++ ~DxvkGraphicsPipelineInstance() { ++ m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipeline, nullptr); ++ } ++ + /** + * \brief Checks for matching pipeline state + * +@@ -159,23 +165,38 @@ namespace dxvk { + && m_renderPass == rp; + } + ++ /** ++ * \brief Sets the pipeline handle ++ * ++ * If a pipeline handle has already been ++ * set up, this method will fail and the new pipeline ++ * handle should be destroyed. ++ * \param [in] pipeline The pipeline ++ */ ++ bool setPipeline(VkPipeline pipeline) { ++ VkPipeline expected = VK_NULL_HANDLE; ++ return m_pipeline.compare_exchange_strong(expected, pipeline); ++ } ++ + /** + * \brief Retrieves pipeline + * \returns The pipeline handle + */ + VkPipeline pipeline() const { +- return m_pipeline; ++ return m_pipeline.load(); + } + + private: + ++ const Rc<vk::DeviceFn> m_vkd; + DxvkGraphicsPipelineStateInfo m_stateVector; + VkRenderPass m_renderPass; +- VkPipeline m_pipeline; ++ std::atomic<VkPipeline> m_pipeline; + + }; + + ++ + /** + * \brief Graphics pipeline + * +@@ -234,20 +255,20 @@ namespace dxvk { + * state. If necessary, a new pipeline will be created. + * \param [in] state Pipeline state vector + * \param [in] renderPass The render pass ++ * \param [in] async Compile asynchronously + * \returns Pipeline handle + */ + VkPipeline getPipelineHandle( + const DxvkGraphicsPipelineStateInfo& state, +- const DxvkRenderPass& renderPass); ++ const Rc<DxvkRenderPass>& renderPass, ++ bool async); ++ ++ VkPipeline compileInstance( ++ const Rc<DxvkGraphicsPipelineInstance>& instance, ++ const Rc<DxvkRenderPass>& renderPass); + + private: + +- struct PipelineStruct { +- DxvkGraphicsPipelineStateInfo stateVector; +- VkRenderPass renderPass; +- VkPipeline pipeline; +- }; +- + Rc<vk::DeviceFn> m_vkd; + DxvkPipelineManager* m_pipeMgr; + +@@ -267,13 +288,13 @@ namespace dxvk { + DxvkGraphicsCommonPipelineStateInfo m_common; + + // List of pipeline instances, shared between threads +- alignas(CACHE_LINE_SIZE) sync::Spinlock m_mutex; +- std::vector<DxvkGraphicsPipelineInstance> m_pipelines; ++ alignas(CACHE_LINE_SIZE) sync::Spinlock m_mutex; ++ std::vector<Rc<DxvkGraphicsPipelineInstance>> m_pipelines; + + // Pipeline handles used for derivative pipelines + VkPipeline m_basePipeline = VK_NULL_HANDLE; + +- const DxvkGraphicsPipelineInstance* findInstance( ++ DxvkGraphicsPipelineInstance* findInstance( + const DxvkGraphicsPipelineStateInfo& state, + VkRenderPass renderPass) const; + +@@ -302,4 +323,4 @@ namespace dxvk { + + }; + +-} +\ No newline at end of file ++} +diff --git a/src/dxvk/dxvk_image.h b/src/dxvk/dxvk_image.h +index f55ae42d..1bd638f0 100644 +--- a/src/dxvk/dxvk_image.h ++++ b/src/dxvk/dxvk_image.h +@@ -435,6 +435,37 @@ namespace dxvk { + return result; + } + ++ /** ++ * \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<vk::DeviceFn> m_vkd; +@@ -443,6 +474,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_pipecompiler.cpp b/src/dxvk/dxvk_pipecompiler.cpp +new file mode 100644 +index 00000000..a70c9908 +--- /dev/null ++++ b/src/dxvk/dxvk_pipecompiler.cpp +@@ -0,0 +1,69 @@ ++#include "dxvk_graphics.h" ++#include "dxvk_pipecompiler.h" ++ ++namespace dxvk { ++ ++ DxvkPipelineCompiler::DxvkPipelineCompiler() { ++ uint32_t sysCpuCount = dxvk::thread::hardware_concurrency(); ++ uint32_t threadCount = sysCpuCount > 2 ? sysCpuCount - 2 : 1; ++ ++ Logger::info(str::format( ++ "DxvkPipelineCompiler: Using ", ++ threadCount, " workers")); ++ ++ // Start the compiler threads ++ m_compilerThreads.resize(threadCount); ++ ++ for (uint32_t i = 0; i < threadCount; i++) { ++ m_compilerThreads.at(i) = dxvk::thread( ++ [this] { this->runCompilerThread(); }); ++ } ++ } ++ ++ ++ DxvkPipelineCompiler::~DxvkPipelineCompiler() { ++ { std::unique_lock<std::mutex> lock(m_compilerLock); ++ m_compilerStop.store(true); ++ } ++ ++ m_compilerCond.notify_all(); ++ for (auto& thread : m_compilerThreads) ++ thread.join(); ++ } ++ ++ ++ void DxvkPipelineCompiler::queueCompilation( ++ const Rc<DxvkGraphicsPipeline>& pipeline, ++ const Rc<DxvkGraphicsPipelineInstance>& instance, ++ const Rc<DxvkRenderPass>& renderPass) { ++ std::unique_lock<std::mutex> lock(m_compilerLock); ++ m_compilerQueue.push({ pipeline, instance, renderPass }); ++ m_compilerCond.notify_one(); ++ } ++ ++ ++ void DxvkPipelineCompiler::runCompilerThread() { ++ env::setThreadName("dxvk-pcompiler"); ++ ++ while (!m_compilerStop.load()) { ++ PipelineEntry entry; ++ ++ { std::unique_lock<std::mutex> lock(m_compilerLock); ++ ++ m_compilerCond.wait(lock, [this] { ++ return m_compilerStop.load() ++ || m_compilerQueue.size() != 0; ++ }); ++ ++ if (m_compilerQueue.size() != 0) { ++ entry = std::move(m_compilerQueue.front()); ++ m_compilerQueue.pop(); ++ } ++ } ++ ++ if (entry.pipeline != nullptr && entry.instance != nullptr) ++ entry.pipeline->compileInstance(entry.instance, entry.renderPass); ++ } ++ } ++ ++} +diff --git a/src/dxvk/dxvk_pipecompiler.h b/src/dxvk/dxvk_pipecompiler.h +new file mode 100644 +index 00000000..26e983cc +--- /dev/null ++++ b/src/dxvk/dxvk_pipecompiler.h +@@ -0,0 +1,60 @@ ++#pragma once ++ ++#include <atomic> ++#include <condition_variable> ++#include <mutex> ++#include <queue> ++ ++#include "../util/thread.h" ++#include "dxvk_include.h" ++ ++namespace dxvk { ++ ++ class DxvkGraphicsPipeline; ++ class DxvkGraphicsPipelineInstance; ++ ++ /** ++ * \brief Pipeline compiler ++ * ++ * Asynchronous pipeline compiler ++ */ ++ class DxvkPipelineCompiler : public RcObject { ++ ++ public: ++ ++ DxvkPipelineCompiler(); ++ ~DxvkPipelineCompiler(); ++ ++ /** ++ * \brief Compiles a pipeline asynchronously ++ * ++ * This should be used to compile graphics ++ * pipeline instances asynchronously. ++ * \param [in] pipeline The pipeline object ++ * \param [in] instance The pipeline instance ++ * \param [in] renderPass ++ */ ++ void queueCompilation( ++ const Rc<DxvkGraphicsPipeline>& pipeline, ++ const Rc<DxvkGraphicsPipelineInstance>& instance, ++ const Rc<DxvkRenderPass>& renderPass); ++ ++ private: ++ ++ struct PipelineEntry { ++ Rc<DxvkGraphicsPipeline> pipeline; ++ Rc<DxvkGraphicsPipelineInstance> instance; ++ Rc<DxvkRenderPass> renderPass; ++ }; ++ ++ std::atomic<bool> m_compilerStop = { false }; ++ std::mutex m_compilerLock; ++ std::condition_variable m_compilerCond; ++ std::queue<PipelineEntry> m_compilerQueue; ++ std::vector<dxvk::thread> m_compilerThreads; ++ ++ void runCompilerThread(); ++ ++ }; ++ ++} +diff --git a/src/dxvk/dxvk_pipemanager.cpp b/src/dxvk/dxvk_pipemanager.cpp +index 682db83d..2783c847 100644 +--- a/src/dxvk/dxvk_pipemanager.cpp ++++ b/src/dxvk/dxvk_pipemanager.cpp +@@ -45,9 +45,13 @@ namespace dxvk { + : m_device (device), + m_cache (new DxvkPipelineCache(device->vkd())) { + std::string useStateCache = env::getEnvVar("DXVK_STATE_CACHE"); ++ std::string useAsync = env::getEnvVar("DXVK_ASYNC"); + + if (useStateCache != "0" && device->config().enableStateCache) + m_stateCache = new DxvkStateCache(device, this, passManager); ++ ++ if (useAsync != "0" || device->config().useAsync) ++ m_compiler = new DxvkPipelineCompiler(); + } + + +diff --git a/src/dxvk/dxvk_pipemanager.h b/src/dxvk/dxvk_pipemanager.h +index fb62cb9a..c3b846cf 100644 +--- a/src/dxvk/dxvk_pipemanager.h ++++ b/src/dxvk/dxvk_pipemanager.h +@@ -5,6 +5,7 @@ + + #include "dxvk_compute.h" + #include "dxvk_graphics.h" ++#include "dxvk_pipecompiler.h" + + namespace dxvk { + +@@ -139,6 +140,7 @@ namespace dxvk { + const DxvkDevice* m_device; + Rc<DxvkPipelineCache> m_cache; + Rc<DxvkStateCache> m_stateCache; ++ Rc<DxvkPipelineCompiler> m_compiler; + + std::atomic<uint32_t> m_numComputePipelines = { 0 }; + std::atomic<uint32_t> m_numGraphicsPipelines = { 0 }; +diff --git a/src/dxvk/dxvk_state_cache.cpp b/src/dxvk/dxvk_state_cache.cpp +index a9249a3a..4d296c19 100644 +--- a/src/dxvk/dxvk_state_cache.cpp ++++ b/src/dxvk/dxvk_state_cache.cpp +@@ -263,7 +263,7 @@ namespace dxvk { + const auto& entry = m_entries[e->second]; + + auto rp = m_passManager->getRenderPass(entry.format); +- pipeline->getPipelineHandle(entry.gpState, *rp); ++ pipeline->getPipelineHandle(entry.gpState, rp, false); + } + } else { + auto pipeline = m_pipeManager->createComputePipeline(item.cs); +diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build +index 64f20998..bd11e390 100644 +--- a/src/dxvk/meson.build ++++ b/src/dxvk/meson.build +@@ -78,6 +78,7 @@ dxvk_src = files([ + 'dxvk_openvr.cpp', + 'dxvk_options.cpp', + 'dxvk_pipecache.cpp', ++ 'dxvk_pipecompiler.cpp', + 'dxvk_pipelayout.cpp', + 'dxvk_pipemanager.cpp', + 'dxvk_queue.cpp', + +diff --git a/src/dxvk/dxvk_options.cpp b/src/dxvk/dxvk_options.cpp +index e1da8a48..9d6b4ee9 100644 +--- a/src/dxvk/dxvk_options.cpp ++++ b/src/dxvk/dxvk_options.cpp +@@ -4,6 +4,7 @@ namespace dxvk { + + DxvkOptions::DxvkOptions(const Config& config) { + enableStateCache = config.getOption<bool> ("dxvk.enableStateCache", true); ++ useAsync = config.getOption<bool> ("dxvk.useAsync", false); + numCompilerThreads = config.getOption<int32_t> ("dxvk.numCompilerThreads", 0); + useRawSsbo = config.getOption<Tristate>("dxvk.useRawSsbo", Tristate::Auto); + useEarlyDiscard = config.getOption<Tristate>("dxvk.useEarlyDiscard", Tristate::Auto); +diff --git a/src/dxvk/dxvk_options.h b/src/dxvk/dxvk_options.h +index 79259875..db2f8a32 100644 +--- a/src/dxvk/dxvk_options.h ++++ b/src/dxvk/dxvk_options.h +@@ -10,6 +10,7 @@ namespace dxvk { + + /// Enable state cache + bool enableStateCache; ++ bool useAsync; + + /// Number of compiler threads + /// when using the state cache |