summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorStelios Tsampas2019-05-13 19:46:53 +0300
committerStelios Tsampas2019-05-13 19:46:53 +0300
commitf47f1a9195cf87d6e1d62a4f64a90770ca6aad15 (patch)
tree2916c67a1f20672f15a54cef9ce6f3b62cf41f50
parent76fe43955669b65c7214aa5e9089d9a865cecc97 (diff)
downloadaur-f47f1a9195cf87d6e1d62a4f64a90770ca6aad15.tar.gz
version 0.11
-rw-r--r--.SRCINFO8
-rw-r--r--PKGBUILD12
-rw-r--r--dxvk-async.patch634
3 files changed, 649 insertions, 5 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 603f266068e2..48cd8a2bd331 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -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
diff --git a/PKGBUILD b/PKGBUILD
index 29e59a15d29b..90850cc8c605 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -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