summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Grande2020-10-04 13:07:04 -0400
committerVincent Grande2020-10-04 13:07:04 -0400
commit08194766967ccc207551447f37e17ed1b219615e (patch)
tree899d454b5eb4002094a716e2fbbee4c08401b767
downloadaur-08194766967ccc207551447f37e17ed1b219615e.tar.gz
initial upload
-rw-r--r--.SRCINFO46
-rw-r--r--PKGBUILD113
-rw-r--r--dxvk-async.patch501
-rw-r--r--extraopts.patch36
4 files changed, 696 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..0d9e3fc20d1b
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,46 @@
+pkgbase = dxvk-mingw-proton-ge-async-git
+ pkgdesc = A Vulkan-based compatibility layer for Direct3D 9/10/11 which allows running 3D applications on Linux using Wine. Windows DLL version)
+ pkgver = 1.5.r3.ga265af74
+ pkgrel = 1
+ url = https://github.com/doitsujin/dxvk
+ arch = x86_64
+ license = zlib/libpng
+ makedepends = ninja
+ makedepends = meson>=0.43
+ makedepends = glslang
+ makedepends = mingw-w64-gcc
+ makedepends = git
+ makedepends = wine
+ depends = vulkan-icd-loader
+ depends = wine>=4.0rc1
+ depends = lib32-vulkan-icd-loader
+ provides = dxvk
+ provides = d9vk
+ provides = dxvk=1.5.r3.ga265af74
+ conflicts = d9vk-mingw-git
+ conflicts = dxvk-mingw-git
+ conflicts = dxvk-mingw
+ conflicts = dxvk-winelib
+ conflicts = d9vk-bin
+ conflicts = d9vk-winelib-git
+ conflicts = dxvk-bin
+ conflicts = dxvk-git
+ conflicts = dxvk-wine32-git
+ conflicts = dxvk-wine64-git
+ conflicts = dxvk-win32-git
+ conflicts = dxvk-win64-git
+ conflicts = dxvk-winelib-git
+ options = !strip
+ options = !buildflags
+ options = staticlibs
+ source = git+https://github.com/doitsujin/dxvk.git
+ source = dxvk-async.patch
+ source = extraopts.patch
+ sha256sums = SKIP
+ sha256sums = SKIP
+ sha256sums = SKIP
+
+pkgname = dxvk-mingw-proton-ge-async-git
+ arch = x86_64
+ conflicts = dxvk-bin
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..ed4a7899b601
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,113 @@
+# Maintainer: Vincent Grande <shoober420@gmail.com>
+# Contributor: loathingkernel <loathingkernel @at gmail .dot com>
+# Contributor: AdriƠ Cereto i MassaguƩ <ssorgatem at gmail.com>
+
+pkgname=('dxvk-mingw-proton-ge-async-git')
+pkgver=1.5.r3.ga265af74
+pkgrel=1
+pkgdesc="A Vulkan-based compatibility layer for Direct3D 9/10/11 which allows running 3D applications on Linux using Wine. Windows DLL version)"
+arch=('x86_64')
+url="https://github.com/doitsujin/dxvk"
+license=('zlib/libpng')
+depends=('vulkan-icd-loader' 'wine>=4.0rc1' 'lib32-vulkan-icd-loader')
+provides=("dxvk" "d9vk" "dxvk=$pkgver")
+makedepends=('ninja' 'meson>=0.43' 'glslang' 'mingw-w64-gcc' 'git' 'wine')
+conflicts=('d9vk-mingw-git' 'dxvk-mingw-git' 'dxvk-mingw' 'dxvk-winelib' 'd9vk-bin' 'd9vk-winelib-git' "dxvk-bin" "dxvk-git" "dxvk-wine32-git" "dxvk-wine64-git" "dxvk-win32-git" "dxvk-win64-git" "dxvk-winelib-git")
+options=(!strip !buildflags staticlibs)
+source=("git+https://github.com/doitsujin/dxvk.git"
+ "dxvk-async.patch"
+ "extraopts.patch")
+sha256sums=("SKIP"
+ "SKIP"
+ "SKIP")
+
+
+pkgver() {
+ cd dxvk
+ git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/v//g'
+}
+
+prepare() {
+ cd dxvk
+
+ # Uncomment to enable extra optimizations
+ # Patch crossfiles with extra optimizations from makepkg.conf
+ patch -p1 -i "$srcdir"/extraopts.patch
+ local dxvk_cflags="$CFLAGS"
+ local dxvk_ldflags="$LDFLAGS"
+ # Filter known bad flags before applying optimizations
+ # If using -march=native and the CPU supports AVX, launching a d3d9
+ # game can cause an Unhandled exception. The cause seems to be the
+ # combination of AVX instructions and tree vectorization (implied by O3),
+ # all tested archictures from sandybridge to haswell are affected.
+ # Disabling AVX (and AVX2 as a side-effect).
+ # Relevant Wine issues
+ # https://bugs.winehq.org/show_bug.cgi?id=45289
+ # https://bugs.winehq.org/show_bug.cgi?id=43516
+ dxvk_cflags+=" -mno-avx"
+ # Filter fstack-protector{ ,-all,-strong} flag for MingW.
+ # https://github.com/Joshua-Ashton/d9vk/issues/476
+ #dxvk_cflags+=" -fno-stack-protector"
+ dxvk_cflags="${dxvk_cflags// -fstack-protector*([\-all|\-strong])/}"
+ # Adjust optimization level in meson arguments. This is ignored
+ # anyway because meson sets its own optimization level.
+ dxvk_cflags="${dxvk_cflags// -O+([0-3s]|fast)/}"
+ # Doesn't compile with these flags in MingW so remove them.
+ # They are also filtered in Wine PKGBUILDs so remove them
+ # for winelib versions too.
+ dxvk_cflags="${dxvk_cflags/ -fno-plt/}"
+ dxvk_ldflags="${dxvk_ldflags/,-z,now/}"
+ dxvk_ldflags="${dxvk_ldflags/,-z,relro/}"
+ sed -i build-win64.txt \
+ -e "s|@CARGS@|\'${dxvk_cflags// /\',\'}\'|g" \
+ -e "s|@LDARGS@|\'${dxvk_ldflags// /\',\'}\'|g"
+ sed -i build-win32.txt \
+ -e "s|@CARGS@|\'${dxvk_cflags// /\',\'}\'|g" \
+ -e "s|@LDARGS@|\'${dxvk_ldflags// /\',\'}\'|g"
+ # Uncomment to enable dxvk async patch.
+ # Enable at your own risk. If you don't know what it is,
+ # and its implications, leave it as is. You have been warned.
+ # I am not liable if anything happens to you by using it.
+ # Patch enables async by default. YOU HAVE BEEN WARNED.
+ patch -p1 -i "$srcdir"/dxvk-async.patch
+}
+
+build() {
+ meson dxvk "build/x64" \
+ --cross-file dxvk/build-win64.txt \
+ --prefix "/usr/share/dxvk/x64" \
+ --bindir "" --libdir "" \
+ --buildtype "release" \
+ --strip \
+ -D enable_tests=false
+ ninja -C "build/x64"
+
+ meson dxvk "build/x32" \
+ --cross-file dxvk/build-win32.txt \
+ --prefix "/usr/share/dxvk/x32" \
+ --bindir "" --libdir "" \
+ --buildtype "release" \
+ --strip \
+ -D enable_tests=false
+ ninja -C "build/x32"
+}
+
+package_dxvk-mingw-proton-ge-async-git() {
+ arch=('x86_64')
+ conflicts=("dxvk-bin")
+ DESTDIR="$pkgdir" ninja -C "build/x32" install
+ DESTDIR="$pkgdir" ninja -C "build/x64" install
+ install -Dm 644 dxvk/setup_dxvk.sh "$pkgdir/usr/share/dxvk/setup_dxvk.sh"
+ mkdir -p "$pkgdir/usr/bin"
+ ln -s /usr/share/dxvk/setup_dxvk.sh "$pkgdir/usr/bin/setup_dxvk"
+ chmod +x "$pkgdir/usr/share/dxvk/setup_dxvk.sh"
+
+ rm -rf /usr/share/steam/compatibilitytools.d/proton-ge-custom*/dist/lib/wine/dxvk/d3d*
+ rm -rf /usr/share/steam/compatibilitytools.d/proton-ge-custom*/dist/lib/wine/dxvk/dxg*
+ rm -rf /usr/share/steam/compatibilitytools.d/proton-ge-custom*/dist/lib64/wine/dxvk/d3d*
+ rm -rf /usr/share/steam/compatibilitytools.d/proton-ge-custom*/dust/lib64/wine/dxvk/dxg*
+
+ ln -s /usr/share/dxvk/x32/* /usr/share/steam/compatibilitytools.d/proton-ge-custom*/dist/lib/wine/dxvk/
+
+ ln -s /usr/share/dxvk/x64/* /usr/share/steam/compatibilitytools.d/proton-ge-custom*/dist/lib64/wine/dxvk/
+}
diff --git a/dxvk-async.patch b/dxvk-async.patch
new file mode 100644
index 000000000000..d877be322826
--- /dev/null
+++ b/dxvk-async.patch
@@ -0,0 +1,501 @@
+diff --git a/meson.build b/meson.build
+index 8fa3ce61..5e4d32d1 100644
+--- a/meson.build
++++ b/meson.build
+@@ -103,7 +103,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 797c4e20..bda90275 100644
+--- a/src/dxvk/dxvk_context.cpp
++++ b/src/dxvk/dxvk_context.cpp
+@@ -3837,7 +3837,9 @@ namespace dxvk {
+ : DxvkContextFlag::GpDirtyStencilRef);
+
+ // Retrieve and bind actual Vulkan pipeline handle
+- m_gpActivePipeline = m_state.gp.pipeline->getPipelineHandle(m_state.gp.state, m_state.om.framebuffer->getRenderPass());
++ m_gpActivePipeline = m_state.gp.pipeline->getPipelineHandle(m_state.gp.state,
++ m_state.om.framebuffer->getRenderPass(),
++ this->checkAsyncCompilationCompat());
+
+ if (unlikely(!m_gpActivePipeline))
+ return false;
+@@ -4093,7 +4095,7 @@ namespace dxvk {
+ }
+
+
+- void DxvkContext::updateFramebuffer() {
++ void DxvkContext::updateFramebuffer(bool isDraw) {
+ if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer)) {
+ m_flags.clr(DxvkContextFlag::GpDirtyFramebuffer);
+
+@@ -4114,6 +4116,11 @@ namespace dxvk {
+ m_state.gp.state.omSwizzle[i] = DxvkOmAttachmentSwizzle(mapping);
+ }
+
++ if (isDraw) {
++ for (uint32_t i = 0; i < fb->numAttachments(); i++)
++ fb->getAttachment(i).view->setRtBindingFrameId(m_device->getCurrentFrameId());
++ }
++
+ m_flags.set(DxvkContextFlag::GpDirtyPipelineState);
+ }
+ }
+@@ -4343,7 +4350,7 @@ namespace dxvk {
+ }
+
+ if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer))
+- this->updateFramebuffer();
++ this->updateFramebuffer(true);
+
+ if (!m_flags.test(DxvkContextFlag::GpRenderPassBound))
+ this->startRenderPass();
+@@ -4772,6 +4779,14 @@ 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;
++ }
+
+ DxvkGraphicsPipeline* DxvkContext::lookupGraphicsPipeline(
+ const DxvkGraphicsPipelineShaders& shaders) {
+diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h
+index 2456b20a..d58f1021 100644
+--- a/src/dxvk/dxvk_context.h
++++ b/src/dxvk/dxvk_context.h
+@@ -1159,7 +1159,7 @@ namespace dxvk {
+ VkDescriptorSet set,
+ const DxvkPipelineLayout* layout);
+
+- void updateFramebuffer();
++ void updateFramebuffer(bool isDraw = false);
+
+ void updateIndexBufferBinding();
+ void updateVertexBufferBindings();
+@@ -1211,6 +1211,8 @@ namespace dxvk {
+
+ void trackDrawBuffer();
+
++ bool checkAsyncCompilationCompat();
++
+ DxvkGraphicsPipeline* lookupGraphicsPipeline(
+ const DxvkGraphicsPipelineShaders& shaders);
+
+diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp
+index 192e340b..96826600 100644
+--- a/src/dxvk/dxvk_graphics.cpp
++++ b/src/dxvk/dxvk_graphics.cpp
+@@ -62,17 +62,21 @@ namespace dxvk {
+
+ VkPipeline DxvkGraphicsPipeline::getPipelineHandle(
+ const DxvkGraphicsPipelineStateInfo& state,
+- const DxvkRenderPass* renderPass) {
++ const DxvkRenderPass* renderPass,
++ bool async) {
+ DxvkGraphicsPipelineInstance* instance = nullptr;
+
+- { std::lock_guard<sync::Spinlock> lock(m_mutex);
++ { //std::lock_guard<sync::Spinlock> lock(m_mutex);
+
+ instance = this->findInstance(state, renderPass);
+
+ if (instance)
+ return instance->pipeline();
+
+- instance = this->createInstance(state, renderPass);
++ if (async && m_pipeMgr->m_compiler != nullptr)
++ m_pipeMgr->m_compiler->queueCompilation(this, state, renderPass);
++ else
++ instance = this->createInstance(state, renderPass);
+ }
+
+ if (!instance)
+@@ -83,13 +87,13 @@ namespace dxvk {
+ }
+
+
+- void DxvkGraphicsPipeline::compilePipeline(
++ bool DxvkGraphicsPipeline::compilePipeline(
+ const DxvkGraphicsPipelineStateInfo& state,
+ const DxvkRenderPass* renderPass) {
+ std::lock_guard<sync::Spinlock> lock(m_mutex);
+
+- if (!this->findInstance(state, renderPass))
+- this->createInstance(state, renderPass);
++ return (this->findInstance(state, renderPass) == nullptr) &&
++ (this->createInstance(state, renderPass) != nullptr);
+ }
+
+
+@@ -103,6 +107,7 @@ namespace dxvk {
+
+ VkPipeline newPipelineHandle = this->createPipeline(state, renderPass);
+
++ std::lock_guard<sync::Spinlock> lock(m_mutex2);
+ m_pipeMgr->m_numGraphicsPipelines += 1;
+ return &m_pipelines.emplace_back(state, renderPass, newPipelineHandle);
+ }
+@@ -111,6 +116,7 @@ namespace dxvk {
+ DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::findInstance(
+ const DxvkGraphicsPipelineStateInfo& state,
+ const DxvkRenderPass* renderPass) {
++ std::lock_guard<sync::Spinlock> lock(m_mutex2);
+ for (auto& instance : m_pipelines) {
+ if (instance.isCompatible(state, renderPass))
+ return &instance;
+diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h
+index 4194599d..c48ee3ed 100644
+--- a/src/dxvk/dxvk_graphics.h
++++ b/src/dxvk/dxvk_graphics.h
+@@ -185,11 +185,13 @@ 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 DxvkRenderPass* renderPass,
++ bool async);
+
+ /**
+ * \brief Compiles a pipeline
+@@ -198,11 +200,16 @@ namespace dxvk {
+ * and stores the result for future use.
+ * \param [in] state Pipeline state vector
+ * \param [in] renderPass The render pass
++ * \returns \c true if compile succeeded
+ */
+- void compilePipeline(
++ bool compilePipeline(
+ const DxvkGraphicsPipelineStateInfo& state,
+ const DxvkRenderPass* renderPass);
+
++ void writePipelineStateToCache(
++ const DxvkGraphicsPipelineStateInfo& state,
++ const DxvkRenderPassFormat& format) const;
++
+ private:
+
+ Rc<vk::DeviceFn> m_vkd;
+@@ -221,6 +228,7 @@ namespace dxvk {
+
+ // List of pipeline instances, shared between threads
+ alignas(CACHE_LINE_SIZE) sync::Spinlock m_mutex;
++ alignas(CACHE_LINE_SIZE) sync::Spinlock m_mutex2;
+ std::vector<DxvkGraphicsPipelineInstance> m_pipelines;
+
+ DxvkGraphicsPipelineInstance* createInstance(
+@@ -248,10 +256,6 @@ namespace dxvk {
+ bool validatePipelineState(
+ const DxvkGraphicsPipelineStateInfo& state) const;
+
+- void writePipelineStateToCache(
+- const DxvkGraphicsPipelineStateInfo& state,
+- const DxvkRenderPassFormat& format) const;
+-
+ void logPipelineState(
+ LogLevel level,
+ const DxvkGraphicsPipelineStateInfo& state) const;
+diff --git a/src/dxvk/dxvk_image.h b/src/dxvk/dxvk_image.h
+index 19b5d85f..3da24c49 100644
+--- a/src/dxvk/dxvk_image.h
++++ b/src/dxvk/dxvk_image.h
+@@ -465,6 +465,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;
+@@ -473,6 +504,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 904082f6..999723cc 100644
+--- a/src/dxvk/dxvk_options.cpp
++++ b/src/dxvk/dxvk_options.cpp
+@@ -3,8 +3,10 @@
+ namespace dxvk {
+
+ DxvkOptions::DxvkOptions(const Config& config) {
++ enableAsync = config.getOption<bool> ("dxvk.enableAsync", true);
+ enableStateCache = config.getOption<bool> ("dxvk.enableStateCache", true);
+ enableOpenVR = config.getOption<bool> ("dxvk.enableOpenVR", true);
++ numAsyncThreads = config.getOption<int32_t> ("dxvk.numAsyncThreads", 0);
+ 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 6843c16f..84e1933f 100644
+--- a/src/dxvk/dxvk_options.h
++++ b/src/dxvk/dxvk_options.h
+@@ -8,12 +8,19 @@ namespace dxvk {
+ DxvkOptions() { }
+ DxvkOptions(const Config& config);
+
++ // Enable async pipelines
++ bool enableAsync;
++
+ /// Enable state cache
+ bool enableStateCache;
+
+ /// Enables OpenVR loading
+ bool enableOpenVR;
+
++ /// Number of compiler threads
++ /// when using async pipelines
++ int32_t numAsyncThreads;
++
+ /// Number of compiler threads
+ /// when using the state cache
+ int32_t numCompilerThreads;
+diff --git a/src/dxvk/dxvk_pipecompiler.cpp b/src/dxvk/dxvk_pipecompiler.cpp
+new file mode 100644
+index 00000000..40218acd
+--- /dev/null
++++ b/src/dxvk/dxvk_pipecompiler.cpp
+@@ -0,0 +1,76 @@
++#include "dxvk_device.h"
++#include "dxvk_graphics.h"
++#include "dxvk_pipecompiler.h"
++
++namespace dxvk {
++
++ DxvkPipelineCompiler::DxvkPipelineCompiler(const DxvkDevice* device) {
++ uint32_t numCpuCores = dxvk::thread::hardware_concurrency();
++ uint32_t numWorkers = ((std::max(1u, numCpuCores) - 1) * 5) / 7;
++
++ if (numWorkers < 1) numWorkers = 1;
++ if (numWorkers > 32) numWorkers = 32;
++
++ if (device->config().numAsyncThreads > 0)
++ numWorkers = device->config().numAsyncThreads;
++
++ Logger::info(str::format("DXVK: Using ", numWorkers, " async compiler threads"));
++
++ // Start the compiler threads
++ m_compilerThreads.resize(numWorkers);
++
++ for (uint32_t i = 0; i < numWorkers; i++) {
++ m_compilerThreads.at(i) = dxvk::thread(
++ [this] { this->runCompilerThread(); });
++ }
++ }
++
++
++ DxvkPipelineCompiler::~DxvkPipelineCompiler() {
++ { std::lock_guard<std::mutex> lock(m_compilerLock);
++ m_compilerStop.store(true);
++ }
++
++ m_compilerCond.notify_all();
++ for (auto& thread : m_compilerThreads)
++ thread.join();
++ }
++
++
++ void DxvkPipelineCompiler::queueCompilation(
++ DxvkGraphicsPipeline* pipeline,
++ const DxvkGraphicsPipelineStateInfo& state,
++ const DxvkRenderPass* renderPass) {
++ std::lock_guard<std::mutex> lock(m_compilerLock);
++ m_compilerQueue.push({ pipeline, state, 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.renderPass != nullptr &&
++ entry.pipeline->compilePipeline(entry.state, entry.renderPass)) {
++ entry.pipeline->writePipelineStateToCache(entry.state, entry.renderPass->format());
++ }
++ }
++ }
++
++}
+diff --git a/src/dxvk/dxvk_pipecompiler.h b/src/dxvk/dxvk_pipecompiler.h
+new file mode 100644
+index 00000000..d7fcc2cf
+--- /dev/null
++++ b/src/dxvk/dxvk_pipecompiler.h
+@@ -0,0 +1,61 @@
++#pragma once
++
++#include <atomic>
++#include <condition_variable>
++#include <mutex>
++#include <queue>
++
++#include "../util/thread.h"
++#include "dxvk_include.h"
++
++namespace dxvk {
++
++ class DxvkDevice;
++ class DxvkGraphicsPipeline;
++ class DxvkGraphicsPipelineStateInfo;
++
++ /**
++ * \brief Pipeline compiler
++ *
++ * Asynchronous pipeline compiler
++ */
++ class DxvkPipelineCompiler : public RcObject {
++
++ public:
++
++ DxvkPipelineCompiler(const DxvkDevice* device);
++ ~DxvkPipelineCompiler();
++
++ /**
++ * \brief Compiles a pipeline asynchronously
++ *
++ * This should be used to compile graphics
++ * pipeline instances asynchronously.
++ * \param [in] pipeline The pipeline object
++ * \param [in] state The pipeline state info object
++ * \param [in] renderPass
++ */
++ void queueCompilation(
++ DxvkGraphicsPipeline* pipeline,
++ const DxvkGraphicsPipelineStateInfo& state,
++ const DxvkRenderPass* renderPass);
++
++ private:
++
++ struct PipelineEntry {
++ DxvkGraphicsPipeline* pipeline = nullptr;
++ DxvkGraphicsPipelineStateInfo state;
++ const DxvkRenderPass* renderPass = nullptr;
++ };
++
++ 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 2e29202e..1e767381 100644
+--- a/src/dxvk/dxvk_pipemanager.cpp
++++ b/src/dxvk/dxvk_pipemanager.cpp
+@@ -9,7 +9,11 @@ namespace dxvk {
+ DxvkRenderPassPool* passManager)
+ : m_device (device),
+ m_cache (new DxvkPipelineCache(device->vkd())) {
++ std::string useAsync = env::getEnvVar("DXVK_ASYNC");
+ std::string useStateCache = env::getEnvVar("DXVK_STATE_CACHE");
++
++ if (useAsync == "1" || device->config().enableAsync)
++ m_compiler = new DxvkPipelineCompiler(device);
+
+ if (useStateCache != "0" && device->config().enableStateCache)
+ m_stateCache = new DxvkStateCache(device, this, passManager);
+diff --git a/src/dxvk/dxvk_pipemanager.h b/src/dxvk/dxvk_pipemanager.h
+index 858928ca..4c12a4dc 100644
+--- a/src/dxvk/dxvk_pipemanager.h
++++ b/src/dxvk/dxvk_pipemanager.h
+@@ -6,6 +6,7 @@
+
+ #include "dxvk_compute.h"
+ #include "dxvk_graphics.h"
++#include "dxvk_pipecompiler.h"
+
+ namespace dxvk {
+
+@@ -95,6 +96,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/meson.build b/src/dxvk/meson.build
+index 1dc113c3..003fb1a7 100644
+--- a/src/dxvk/meson.build
++++ b/src/dxvk/meson.build
+@@ -83,6 +83,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/extraopts.patch b/extraopts.patch
new file mode 100644
index 000000000000..3faec7e35d55
--- /dev/null
+++ b/extraopts.patch
@@ -0,0 +1,36 @@
+diff --git a/build-win32.txt b/build-win32.txt
+index 0865fc5e..b93a280f 100644
+--- a/build-win32.txt
++++ b/build-win32.txt
+@@ -5,10 +5,10 @@ ar = 'i686-w64-mingw32-ar'
+ strip = 'i686-w64-mingw32-strip'
+
+ [properties]
+-c_args=['-msse', '-msse2']
+-cpp_args=['-msse', '-msse2']
+-c_link_args = ['-static', '-static-libgcc']
+-cpp_link_args = ['-static', '-static-libgcc', '-static-libstdc++']
++c_args=['-msse', '-msse2', @CARGS@]
++cpp_args=['-msse', '-msse2', @CARGS@]
++c_link_args = ['-static', '-static-libgcc', @LDARGS@]
++cpp_link_args = ['-static', '-static-libgcc', '-static-libstdc++', @LDARGS@]
+ needs_exe_wrapper = true
+
+ [host_machine]
+diff --git a/build-win64.txt b/build-win64.txt
+index 2a7fbee3..9e3bfc2e 100644
+--- a/build-win64.txt
++++ b/build-win64.txt
+@@ -5,8 +5,10 @@ ar = 'x86_64-w64-mingw32-ar'
+ strip = 'x86_64-w64-mingw32-strip'
+
+ [properties]
+-c_link_args = ['-static', '-static-libgcc']
+-cpp_link_args = ['-static', '-static-libgcc', '-static-libstdc++']
++c_args=[@CARGS@]
++cpp_args=[@CARGS@]
++c_link_args = ['-static', '-static-libgcc', @LDARGS@]
++cpp_link_args = ['-static', '-static-libgcc', '-static-libstdc++', @LDARGS@]
+ needs_exe_wrapper = true
+
+ [host_machine]