summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorStelios Tsampas2019-12-15 01:45:05 +0200
committerStelios Tsampas2019-12-15 01:45:05 +0200
commite2fc54e0c24a50beef281f4edc11050387a6983a (patch)
tree0d0ce8653a8a8474031e4abfc648004a6b92a7a8
parentccd8f512f1a6c7c5a7aed45cbcec9d62766fdc0d (diff)
downloadaur-e2fc54e0c24a50beef281f4edc11050387a6983a.tar.gz
version 0.40. Enable extra optimizations.
-rw-r--r--.SRCINFO10
-rw-r--r--PKGBUILD44
-rw-r--r--dxvk-async.patch373
-rw-r--r--dxvk-mangohud.patch665
-rw-r--r--extraopts.patch70
5 files changed, 866 insertions, 296 deletions
diff --git a/.SRCINFO b/.SRCINFO
index ab6d15ff9730..eb04e3cce0ec 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.40.rc.p
+ pkgver = 0.40
pkgrel = 1
url = https://github.com/Joshua-Ashton/d9vk
arch = x86_64
@@ -17,12 +17,16 @@ pkgbase = d9vk-mingw
depends = bash
provides = d9vk
conflicts = d9vk
- source = git+https://github.com/Joshua-Ashton/d9vk.git#tag=0.40-rc-p
+ source = git+https://github.com/Joshua-Ashton/d9vk.git#tag=0.40
source = setup_d9vk
source = dxvk-async.patch
+ source = dxvk-mangohud.patch
+ source = extraopts.patch
sha256sums = SKIP
sha256sums = 7147644664ef33d04f7b18683c47be95b5664c57cf6d63fdc019d915deebd37a
- sha256sums = 757fd5ca3faf476730891541de408593450fe4b490cc87bd90b342826d66c564
+ sha256sums = 6ff286091c20327e67252e1e6812830a42c990d1ee56541023eb217712209f3c
+ sha256sums = 2e335237623aaf006f8814fc9712f3a4be0d678cd0714879a3a4545f3bbf41ce
+ sha256sums = acde3a23166f79fa87ab090200be2aabaf16e5876ce19b8270ad1179bb0bc7a5
pkgname = d9vk-mingw
diff --git a/PKGBUILD b/PKGBUILD
index 8885ce573661..41de98daede5 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,8 +1,8 @@
# Maintainer: loathingkernel <loathingkernel @at gmail .dot com>
pkgname=d9vk-mingw
-_tag=0.40-rc-p
-pkgver=${_tag//-/.}
+_tag=0.40
+pkgver=${_tag//-/_}
pkgrel=1
pkgdesc="A d3d9 to vk layer based off DXVK's codebase, mingw version"
arch=('x86_64')
@@ -16,20 +16,56 @@ source=(
"git+https://github.com/Joshua-Ashton/d9vk.git#tag=$_tag"
"setup_d9vk"
"dxvk-async.patch"
+ "dxvk-mangohud.patch"
+ "extraopts.patch"
)
sha256sums=(
"SKIP"
"7147644664ef33d04f7b18683c47be95b5664c57cf6d63fdc019d915deebd37a"
- "757fd5ca3faf476730891541de408593450fe4b490cc87bd90b342826d66c564"
+ "6ff286091c20327e67252e1e6812830a42c990d1ee56541023eb217712209f3c"
+ "2e335237623aaf006f8814fc9712f3a4be0d678cd0714879a3a4545f3bbf41ce"
+ "acde3a23166f79fa87ab090200be2aabaf16e5876ce19b8270ad1179bb0bc7a5"
)
prepare() {
cd d9vk
+ # 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 either AVX (and AVX2 as a side-effect) or tree
+ # vectorization fixes the issue. I am not sure which one is better
+ # to disable so below you can choose. Append either of these flags.
+ # Relevant Wine issues
+ # https://bugs.winehq.org/show_bug.cgi?id=45289
+ # https://bugs.winehq.org/show_bug.cgi?id=43516
+ CFLAGS+=" -mno-avx"
+ # CFLAGS+=" -fno-tree-vectorize"
+ # Patch crossfiles with extra optimizations from makepkg.conf
+ # If building fails, comment the line below to disable them.
+ patch -p1 -i ../extraopts.patch
+ # Adjust optimization level in meson arguments. This is ignored
+ # anyways because meson sets its own optimization level.
+ CFLAGS="${CFLAGS/ -O*([0-3])/}"
+ # 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.
+ CFLAGS="${CFLAGS/ -fno-plt/}"
+ LDFLAGS="${LDFLAGS/,-z,relro,-z,now/}"
+ sed -i build-win64.txt \
+ -e "s|@CARGS@|\'${CFLAGS// /\',\'}\'|g" \
+ -e "s|@LDARGS@|\'${LDFLAGS// /\',\'}\'|g"
+ 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,
# 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 ../dxvk-async.patch
+ # Uncomment to enable Mango HUD for dxvk
+ #patch -p1 -i ../dxvk-mangohud.patch
}
build() {
@@ -38,6 +74,7 @@ build() {
--prefix "/usr/share/d9vk/x64" \
--bindir "" --libdir "" \
--buildtype "release" \
+ --optimization=3 \
--strip \
-Denable_d3d10=false \
-Denable_d3d11=false \
@@ -50,6 +87,7 @@ build() {
--prefix "/usr/share/d9vk/x32" \
--bindir "" --libdir "" \
--buildtype "release" \
+ --optimization=3 \
--strip \
-Denable_d3d10=false \
-Denable_d3d11=false \
diff --git a/dxvk-async.patch b/dxvk-async.patch
index 21bf8325ba7c..19c506c4964f 100644
--- a/dxvk-async.patch
+++ b/dxvk-async.patch
@@ -1,14 +1,15 @@
-From 653b6b9d25d41df41acb9d5d50e06bf8f45cc56c Mon Sep 17 00:00:00 2001
+From dfac76f528d0fc88ecbfe27ccb87483840828b9b Mon Sep 17 00:00:00 2001
From: Tk-Glitch <ti3nou@gmail.com>
-Date: Mon, 15 Jul 2019 05:46:41 +0200
-Subject: async patchset rebase against a93dd74f
+Date: Sat, 28 Sep 2019 18:59:04 +0200
+Subject: Async pipecompiler rebase against DXVK
+ d128d776ad906d6b8d3941eda7b7ee679346dbaf
diff --git a/meson.build b/meson.build
-index f7dcd491..31665f93 100644
+index 9a519e46..dea82ee1 100644
--- a/meson.build
+++ b/meson.build
-@@ -90,7 +90,7 @@ else
+@@ -101,7 +101,7 @@ else
endif
dxvk_version = vcs_tag(
@@ -18,10 +19,10 @@ index f7dcd491..31665f93 100644
output: 'version.h')
diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp
-index 6df6e3c3..bb802c96 100644
+index b4f679fa..13c86d8f 100644
--- a/src/dxvk/dxvk_context.cpp
+++ b/src/dxvk/dxvk_context.cpp
-@@ -648,7 +648,7 @@ namespace dxvk {
+@@ -606,7 +606,7 @@ namespace dxvk {
const Rc<DxvkImageView>& imageView,
VkImageAspectFlags clearAspects,
VkClearValue clearValue) {
@@ -30,7 +31,7 @@ index 6df6e3c3..bb802c96 100644
// Prepare attachment ops
DxvkColorAttachmentOps colorOp;
-@@ -2406,7 +2406,7 @@ namespace dxvk {
+@@ -2433,7 +2433,7 @@ namespace dxvk {
VkExtent3D extent,
VkImageAspectFlags aspect,
VkClearValue value) {
@@ -39,16 +40,16 @@ index 6df6e3c3..bb802c96 100644
// Find out if the render target view is currently bound,
// so that we can avoid spilling the render pass if it is.
-@@ -3527,7 +3527,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) {
-@@ -3794,7 +3794,7 @@ namespace dxvk {
+@@ -3652,7 +3652,7 @@ 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;
+@@ -3969,7 +3969,7 @@ namespace dxvk {
}
@@ -56,8 +57,8 @@ index 6df6e3c3..bb802c96 100644
+ void DxvkContext::updateFramebuffer(bool isDraw) {
if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer)) {
m_flags.clr(DxvkContextFlag::GpDirtyFramebuffer);
-
-@@ -3813,6 +3813,11 @@ namespace dxvk {
+
+@@ -3988,6 +3988,11 @@ namespace dxvk {
: VkComponentMapping();
}
@@ -69,7 +70,7 @@ index 6df6e3c3..bb802c96 100644
m_flags.set(DxvkContextFlag::GpDirtyPipelineState);
}
}
-@@ -4040,7 +4045,7 @@ namespace dxvk {
+@@ -4205,7 +4210,7 @@ namespace dxvk {
template<bool Indexed>
void DxvkContext::commitGraphicsState() {
if (m_flags.test(DxvkContextFlag::GpDirtyFramebuffer))
@@ -78,7 +79,7 @@ index 6df6e3c3..bb802c96 100644
if (!m_flags.test(DxvkContextFlag::GpRenderPassBound))
this->startRenderPass();
-@@ -4288,4 +4293,13 @@ namespace dxvk {
+@@ -4465,4 +4470,13 @@ namespace dxvk {
}
}
@@ -95,10 +96,10 @@ index 6df6e3c3..bb802c96 100644
+ }
+}
diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h
-index 79a818e9..935cc5a4 100644
+index f54f5321..0984d085 100644
--- a/src/dxvk/dxvk_context.h
+++ b/src/dxvk/dxvk_context.h
-@@ -1138,7 +1138,7 @@ namespace dxvk {
+@@ -1141,7 +1141,7 @@ namespace dxvk {
VkDescriptorSet set,
const DxvkPipelineLayout* layout);
@@ -107,50 +108,21 @@ index 79a818e9..935cc5a4 100644
void updateIndexBufferBinding();
void updateVertexBufferBindings();
-@@ -1163,6 +1163,8 @@ namespace dxvk {
-
- void commitGraphicsPostBarriers();
+@@ -1180,6 +1180,7 @@ namespace dxvk {
+
+ void trackDrawBuffer();
+ 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 92403199..367b955c 100644
+index 5e05b425..8586c794 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 {
+@@ -75,8 +75,6 @@ namespace dxvk {
DxvkGraphicsPipeline::~DxvkGraphicsPipeline() {
@@ -159,151 +131,38 @@ index 92403199..367b955c 100644
}
-@@ -100,8 +99,9 @@ namespace dxvk {
+@@ -96,7 +94,8 @@ namespace dxvk {
VkPipeline DxvkGraphicsPipeline::getPipelineHandle(
const DxvkGraphicsPipelineStateInfo& state,
-- const DxvkRenderPass& renderPass) {
-- VkRenderPass renderPassHandle = renderPass.getDefaultHandle();
-+ const Rc<DxvkRenderPass>& renderPass,
+- const DxvkRenderPass* renderPass) {
++ const DxvkRenderPass* renderPass,
+ bool async) {
-+ VkRenderPass renderPassHandle = renderPass->getDefaultHandle();
-
- VkPipeline newPipelineHandle = VK_NULL_HANDLE;
+ DxvkGraphicsPipelineInstance* instance = nullptr;
-@@ -119,26 +119,47 @@ namespace dxvk {
+ { std::lock_guard<sync::Spinlock> lock(m_mutex);
+@@ -105,10 +104,13 @@ 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);
-+ Rc<DxvkGraphicsPipelineInstance> newPipeline =
-+ new DxvkGraphicsPipelineInstance(m_pipeMgr->m_device->vkd(),
-+ state, renderPassHandle, VK_NULL_HANDLE);
+ if (instance)
+ return instance->pipeline();
+-
+- instance = this->createInstance(state, renderPass);
+
+ if (async && m_pipeMgr->m_compiler != nullptr)
-+ m_pipeMgr->m_compiler->queueCompilation(this, newPipeline, renderPass);
++ m_pipeMgr->m_compiler->queueCompilation(this, state, 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;
++ instance = this->createInstance(state, renderPass);
}
-
-- if (newPipelineHandle != VK_NULL_HANDLE)
-- this->writePipelineStateToCache(state, renderPass.format());
-+ return newPipelineHandle;
-+ }
-+
-+
-+ VkPipeline DxvkGraphicsPipeline::compileInstance(
-+ const Rc<DxvkGraphicsPipelineInstance>& instance,
-+ const Rc<DxvkRenderPass>& renderPass) {
-+ VkPipeline newPipelineHandle = this->compilePipeline(
-+ instance->m_stateVector, *renderPass);
-+
-+ if (!instance->setPipeline(newPipelineHandle)) {
-+ m_vkd->vkDestroyPipeline(m_vkd->device(), newPipelineHandle, nullptr);
-+ } else {
-+ if (newPipelineHandle != VK_NULL_HANDLE)
-+ this->writePipelineStateToCache(instance->m_stateVector, renderPass->format());
-+ }
-
- return newPipelineHandle;
- }
-
-
-- const DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::findInstance(
-+ 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;
+-
++
+ if (!instance)
+ return VK_NULL_HANDLE;
+
diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h
-index 976cf590..2d0fbd2b 100644
+index 168e9714..07f0883d 100644
--- a/src/dxvk/dxvk_graphics.h
+++ b/src/dxvk/dxvk_graphics.h
-@@ -132,8 +132,8 @@ namespace dxvk {
- * Stores a state vector and the
- * corresponding pipeline handle.
- */
-- class DxvkGraphicsPipelineInstance {
--
-+ class DxvkGraphicsPipelineInstance: public RcObject {
-+ friend class DxvkGraphicsPipeline;
- public:
-
- DxvkGraphicsPipelineInstance()
-@@ -141,13 +141,19 @@ namespace dxvk {
- m_renderPass (VK_NULL_HANDLE),
- m_pipeline (VK_NULL_HANDLE) { }
- 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
- *
-@@ -162,23 +168,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
- *
-@@ -237,19 +258,19 @@ namespace dxvk {
+@@ -247,11 +247,13 @@ namespace dxvk {
* state. If necessary, a new pipeline will be created.
* \param [in] state Pipeline state vector
* \param [in] renderPass The render pass
@@ -312,50 +171,17 @@ index 976cf590..2d0fbd2b 100644
*/
VkPipeline getPipelineHandle(
const DxvkGraphicsPipelineStateInfo& state,
-- const DxvkRenderPass& renderPass);
-+ const Rc<DxvkRenderPass>& renderPass,
+- const DxvkRenderPass* renderPass);
++ const DxvkRenderPass* renderPass,
+ bool async);
-- private:
-+ VkPipeline compileInstance(
-+ const Rc<DxvkGraphicsPipelineInstance>& instance,
-+ const Rc<DxvkRenderPass>& renderPass);
-
-- struct PipelineStruct {
-- DxvkGraphicsPipelineStateInfo stateVector;
-- VkRenderPass renderPass;
-- VkPipeline pipeline;
-- };
-+ private:
-
- Rc<vk::DeviceFn> m_vkd;
- DxvkPipelineManager* m_pipeMgr;
-@@ -270,10 +291,10 @@ 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;
-
-- const DxvkGraphicsPipelineInstance* findInstance(
-+ DxvkGraphicsPipelineInstance* findInstance(
- const DxvkGraphicsPipelineStateInfo& state,
- VkRenderPass renderPass) const;
-
-@@ -301,4 +322,4 @@ namespace dxvk {
-
- };
-
--}
-\ No newline at end of file
-+}
+ /**
+ * \brief Compiles a pipeline
diff --git a/src/dxvk/dxvk_image.h b/src/dxvk/dxvk_image.h
-index 1035a23d..209f4f06 100644
+index 8f82d65a..156f6054 100644
--- a/src/dxvk/dxvk_image.h
+++ b/src/dxvk/dxvk_image.h
-@@ -439,6 +439,37 @@ namespace dxvk {
+@@ -442,6 +442,37 @@ namespace dxvk {
return result;
}
@@ -393,7 +219,7 @@ index 1035a23d..209f4f06 100644
private:
Rc<vk::DeviceFn> m_vkd;
-@@ -447,6 +478,9 @@ namespace dxvk {
+@@ -450,6 +481,9 @@ namespace dxvk {
DxvkImageViewCreateInfo m_info;
VkImageView m_views[ViewCount];
@@ -404,19 +230,19 @@ index 1035a23d..209f4f06 100644
};
diff --git a/src/dxvk/dxvk_options.cpp b/src/dxvk/dxvk_options.cpp
-index f18fd70f..e051c06d 100644
+index 394feabb..df05ecd8 100644
--- a/src/dxvk/dxvk_options.cpp
+++ b/src/dxvk/dxvk_options.cpp
@@ -5,6 +5,7 @@ namespace dxvk {
DxvkOptions::DxvkOptions(const Config& config) {
enableStateCache = config.getOption<bool> ("dxvk.enableStateCache", true);
- enableTransferQueue = config.getOption<bool> ("dxvk.enableTransferQueue", true);
-+ useAsync = config.getOption<bool> ("dxvk.useAsync", false);
+ enableOpenVR = config.getOption<bool> ("dxvk.enableOpenVR", true);
++ useAsync = config.getOption<bool> ("dxvk.useAsync", true);
numCompilerThreads = config.getOption<int32_t> ("dxvk.numCompilerThreads", 0);
+ asyncPresent = config.getOption<Tristate>("dxvk.asyncPresent", Tristate::Auto);
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 aec003e4..0fbb6dd0 100644
+index 447294b5..571d6c15 100644
--- a/src/dxvk/dxvk_options.h
+++ b/src/dxvk/dxvk_options.h
@@ -10,6 +10,7 @@ namespace dxvk {
@@ -429,7 +255,7 @@ index aec003e4..0fbb6dd0 100644
bool enableTransferQueue;
diff --git a/src/dxvk/dxvk_pipecompiler.cpp b/src/dxvk/dxvk_pipecompiler.cpp
new file mode 100644
-index 00000000..a70c9908
+index 00000000..cfef228a
--- /dev/null
+++ b/src/dxvk/dxvk_pipecompiler.cpp
@@ -0,0 +1,69 @@
@@ -468,11 +294,11 @@ index 00000000..a70c9908
+
+
+ void DxvkPipelineCompiler::queueCompilation(
-+ const Rc<DxvkGraphicsPipeline>& pipeline,
-+ const Rc<DxvkGraphicsPipelineInstance>& instance,
-+ const Rc<DxvkRenderPass>& renderPass) {
++ DxvkGraphicsPipeline* pipeline,
++ const DxvkGraphicsPipelineStateInfo& state,
++ const DxvkRenderPass* renderPass) {
+ std::unique_lock<std::mutex> lock(m_compilerLock);
-+ m_compilerQueue.push({ pipeline, instance, renderPass });
++ m_compilerQueue.push({ pipeline, state, renderPass });
+ m_compilerCond.notify_one();
+ }
+
@@ -496,15 +322,15 @@ index 00000000..a70c9908
+ }
+ }
+
-+ if (entry.pipeline != nullptr && entry.instance != nullptr)
-+ entry.pipeline->compileInstance(entry.instance, entry.renderPass);
++ if (entry.pipeline != nullptr && entry.renderPass != nullptr)
++ entry.pipeline->compilePipeline(entry.state, entry.renderPass);
+ }
+ }
+
+}
diff --git a/src/dxvk/dxvk_pipecompiler.h b/src/dxvk/dxvk_pipecompiler.h
new file mode 100644
-index 00000000..26e983cc
+index 00000000..a82fff60
--- /dev/null
+++ b/src/dxvk/dxvk_pipecompiler.h
@@ -0,0 +1,60 @@
@@ -521,7 +347,7 @@ index 00000000..26e983cc
+namespace dxvk {
+
+ class DxvkGraphicsPipeline;
-+ class DxvkGraphicsPipelineInstance;
++ class DxvkGraphicsPipelineStateInfo;
+
+ /**
+ * \brief Pipeline compiler
@@ -541,20 +367,20 @@ index 00000000..26e983cc
+ * This should be used to compile graphics
+ * pipeline instances asynchronously.
+ * \param [in] pipeline The pipeline object
-+ * \param [in] instance The pipeline instance
++ * \param [in] state The pipeline state info object
+ * \param [in] renderPass
+ */
+ void queueCompilation(
-+ const Rc<DxvkGraphicsPipeline>& pipeline,
-+ const Rc<DxvkGraphicsPipelineInstance>& instance,
-+ const Rc<DxvkRenderPass>& renderPass);
++ DxvkGraphicsPipeline* pipeline,
++ const DxvkGraphicsPipelineStateInfo& state,
++ const DxvkRenderPass* renderPass);
+
+ private:
+
+ struct PipelineEntry {
-+ Rc<DxvkGraphicsPipeline> pipeline;
-+ Rc<DxvkGraphicsPipelineInstance> instance;
-+ Rc<DxvkRenderPass> renderPass;
++ DxvkGraphicsPipeline* pipeline = nullptr;
++ DxvkGraphicsPipelineStateInfo state;
++ const DxvkRenderPass* renderPass = nullptr;
+ };
+
+ std::atomic<bool> m_compilerStop = { false };
@@ -569,7 +395,7 @@ index 00000000..26e983cc
+
+}
diff --git a/src/dxvk/dxvk_pipemanager.cpp b/src/dxvk/dxvk_pipemanager.cpp
-index 682db83d..cb7e993b 100644
+index 378bb3d1..3323f926 100644
--- a/src/dxvk/dxvk_pipemanager.cpp
+++ b/src/dxvk/dxvk_pipemanager.cpp
@@ -45,9 +45,13 @@ namespace dxvk {
@@ -587,10 +413,10 @@ index 682db83d..cb7e993b 100644
diff --git a/src/dxvk/dxvk_pipemanager.h b/src/dxvk/dxvk_pipemanager.h
-index fb62cb9a..c3b846cf 100644
+index f0087d15..29e758c7 100644
--- a/src/dxvk/dxvk_pipemanager.h
+++ b/src/dxvk/dxvk_pipemanager.h
-@@ -5,6 +5,7 @@
+@@ -6,6 +6,7 @@
#include "dxvk_compute.h"
#include "dxvk_graphics.h"
@@ -598,7 +424,7 @@ index fb62cb9a..c3b846cf 100644
namespace dxvk {
-@@ -139,6 +140,7 @@ namespace dxvk {
+@@ -107,6 +108,7 @@ namespace dxvk {
const DxvkDevice* m_device;
Rc<DxvkPipelineCache> m_cache;
Rc<DxvkStateCache> m_stateCache;
@@ -606,24 +432,11 @@ index fb62cb9a..c3b846cf 100644
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 604434c2..bf90af2d 100644
+index 867f3002..f4c4336c 100644
--- a/src/dxvk/meson.build
+++ b/src/dxvk/meson.build
-@@ -77,6 +77,7 @@ dxvk_src = files([
+@@ -82,6 +82,7 @@ dxvk_src = files([
'dxvk_openvr.cpp',
'dxvk_options.cpp',
'dxvk_pipecache.cpp',
@@ -631,23 +444,3 @@ index 604434c2..bf90af2d 100644
'dxvk_pipelayout.cpp',
'dxvk_pipemanager.cpp',
'dxvk_queue.cpp',
-diff --git a/dxvk.conf b/dxvk.conf
-index 25d27cb9..4e409075 100644
---- a/dxvk.conf
-+++ b/dxvk.conf
-@@ -159,6 +159,15 @@
- # dxvk.numCompilerThreads = 0
-
-
-+# Compile pipelines asynchronously if possible. This may reduce stuttering
-+# in some games, but may also introduce rendering issues that might become
-+# apparent over time. Do not report bugs with this option enabled.
-+#
-+# Supported values: True, False
-+
-+# dxvk.useAsync = False
-+
-+
- # Toggles raw SSBO usage.
- #
- # Uses storage buffers to implement raw and structured buffer
diff --git a/dxvk-mangohud.patch b/dxvk-mangohud.patch
new file mode 100644
index 000000000000..13fbdee8db9a
--- /dev/null
+++ b/dxvk-mangohud.patch
@@ -0,0 +1,665 @@
+diff --git a/README.md b/README.md
+index 25d03230..e8a53432 100644
+--- a/README.md
++++ b/README.md
+@@ -1,3 +1,29 @@
++# MangoHud Specific
++
++- `DXVK_HUD_OFFSET_X` Set X offset of the DVXK Hud.
++- `DXVK_HUD_OFFSET_Y` Set Y offset of the DVXK Hud.
++- `DXVK_LOG_TO_FILE` Turn on logging and select path/filename (Fps,Cpu load,Gpu load)
++- Logging Gpu load requires either mangogpuload or gpuload hud options
++
++# Hud options
++- `mangogpuload` : Shows current gpu load.
++- `mangocpuload` : Shows current cpu load.
++
++# Keybinds
++- `F2` : Toggle Logging on/off
++- `F12` : Toggle Hud on/off
++
++# MangoLog file
++
++ When you press F2, a file is created with your chosen name + date/time stamp.
++ this file can be uploaded to https://flightlessmango.com/logs/new to create graphs automatically.
++ you can share the created page with others, just link it.
++
++ #### Multiple log files
++
++ It's possible to upload multiple files, you can rename them to your preferred names and upload them in a batch.
++ The graphs will then use those names in the data.
++
+ # DXVK
+
+ A Vulkan-based translation layer for Direct3D 10/11 which allows running 3D applications on Linux using Wine.
+diff --git a/src/dxvk/dxvk_cpu.h b/src/dxvk/dxvk_cpu.h
+new file mode 100644
+index 00000000..b2c8736a
+--- /dev/null
++++ b/src/dxvk/dxvk_cpu.h
+@@ -0,0 +1,172 @@
++#include <cmath>
++#include <iomanip>
++#include <array>
++#include <vector>
++#include <algorithm>
++#include <iterator>
++#include <thread>
++#include <sstream>
++#include <fstream>
++using namespace std;
++
++const int NUM_CPU_STATES = 10;
++
++struct Cpus{
++ size_t num;
++ string name;
++ float value;
++ string output;
++};
++
++size_t numCpuCores = dxvk::thread::hardware_concurrency();
++size_t arraySize = numCpuCores + 1;
++std::vector<Cpus> cpuArray;
++
++void coreCounting(){
++ cpuArray.push_back({0, "CPU:"});
++ for (size_t i = 0; i < arraySize; i++) {
++ size_t offset = i;
++ stringstream ss;
++ ss << "CPU" << offset << ":";
++ string cpuNameString = ss.str();
++ cpuArray.push_back({i+1 , cpuNameString});
++ }
++}
++
++std::string m_cpuUtilizationString;
++
++enum CPUStates
++{
++ S_USER = 0,
++ S_NICE,
++ S_SYSTEM,
++ S_IDLE,
++ S_IOWAIT,
++ S_IRQ,
++ S_SOFTIRQ,
++ S_STEAL,
++ S_GUEST,
++ S_GUEST_NICE
++};
++
++typedef struct CPUData
++{
++ std::string cpu;
++ size_t times[NUM_CPU_STATES];
++} CPUData;
++
++void ReadStatsCPU(std::vector<CPUData> & entries)
++{
++ std::ifstream fileStat("/proc/stat");
++
++ std::string line;
++
++ const std::string STR_CPU("cpu");
++ const std::size_t LEN_STR_CPU = STR_CPU.size();
++ const std::string STR_TOT("tot");
++
++ while(std::getline(fileStat, line))
++ {
++ // cpu stats line found
++ if(!line.compare(0, LEN_STR_CPU, STR_CPU))
++ {
++ std::istringstream ss(line);
++
++ // store entry
++ entries.emplace_back(CPUData());
++ CPUData & entry = entries.back();
++
++ // read cpu label
++ ss >> entry.cpu;
++
++ if(entry.cpu.size() > LEN_STR_CPU)
++ entry.cpu.erase(0, LEN_STR_CPU);
++ else
++ entry.cpu = STR_TOT;
++
++ // read times
++ for(int i = 0; i < NUM_CPU_STATES; ++i)
++ ss >> entry.times[i];
++ }
++ }
++}
++
++size_t GetIdleTime(const CPUData & e)
++{
++ return e.times[S_IDLE] +
++ e.times[S_IOWAIT];
++}
++
++size_t GetActiveTime(const CPUData & e)
++{
++ return e.times[S_USER] +
++ e.times[S_NICE] +
++ e.times[S_SYSTEM] +
++ e.times[S_IRQ] +
++ e.times[S_SOFTIRQ] +
++ e.times[S_STEAL] +
++ e.times[S_GUEST] +
++ e.times[S_GUEST_NICE];
++}
++
++void PrintStats(const std::vector<CPUData> & entries1, const std::vector<CPUData> & entries2)
++{
++ const size_t NUM_ENTRIES = entries1.size();
++
++ for(size_t i = 0; i < NUM_ENTRIES; ++i)
++ {
++ const CPUData & e1 = entries1[i];
++ const CPUData & e2 = entries2[i];
++
++ const float ACTIVE_TIME = static_cast<float>(GetActiveTime(e2) - GetActiveTime(e1));
++ const float IDLE_TIME = static_cast<float>(GetIdleTime(e2) - GetIdleTime(e1));
++ const float TOTAL_TIME = ACTIVE_TIME + IDLE_TIME;
++
++ cpuArray[i].value = (truncf(100.f * ACTIVE_TIME / TOTAL_TIME) * 10 / 10);
++ }
++}
++
++int getCpuUsage()
++{
++ std::vector<CPUData> entries1;
++ std::vector<CPUData> entries2;
++
++ // snapshot 1
++ ReadStatsCPU(entries1);
++
++ // 100ms pause
++ std::this_thread::sleep_for(std::chrono::milliseconds(100));
++
++ // snapshot 2
++ ReadStatsCPU(entries2);
++
++ // print output
++ PrintStats(entries1, entries2);
++
++ return 0;
++}
++
++
++void updateCpuStrings(){
++ for (size_t i = 0; i < arraySize; i++) {
++ size_t spacing = 10;
++ string value = to_string(cpuArray[i].value);
++ value.erase( value.find_last_not_of('0') + 1, std::string::npos );
++ size_t correctionValue = (spacing - cpuArray[i].name.length()) - value.length();
++ string correction = "";
++ for (size_t i = 0; i < correctionValue; i++) {
++ correction.append(" ");
++ }
++ stringstream ss;
++ if (i < 11) {
++ if (i == 0) {
++ ss << cpuArray[i].name << correction << cpuArray[i].value << "%";
++ } else {
++ ss << cpuArray[i].name << correction << cpuArray[i].value << "%";
++ }
++ } else {
++ ss << cpuArray[i].name << correction << cpuArray[i].value << "%";
++ }
++ cpuArray[i].output = ss.str();
++ }
++ }
+\ No newline at end of file
+diff --git a/src/dxvk/hud/dxvk_hud.cpp b/src/dxvk/hud/dxvk_hud.cpp
+index 4fcd3bd2..b6e23c4c 100644
+--- a/src/dxvk/hud/dxvk_hud.cpp
++++ b/src/dxvk/hud/dxvk_hud.cpp
+@@ -1,9 +1,13 @@
+-#include <cstring>
+ #include <version.h>
+-
+ #include "dxvk_hud.h"
++#include <windows.h>
++#include <time.h>
+
+ namespace dxvk::hud {
++ float Hud::offset_x_float = 0;
++ float Hud::offset_y_float = 0;
++ bool show_hud = true;
++ int lastPress;
+
+ Hud::Hud(
+ const Rc<DxvkDevice>& device,
+@@ -60,6 +64,14 @@ namespace dxvk::hud {
+
+ Rc<Hud> Hud::createHud(const Rc<DxvkDevice>& device) {
+ std::string hudElements = env::getEnvVar("DXVK_HUD");
++ std::string offset_x = env::getEnvVar("DXVK_HUD_OFFSET_X");
++ std::string offset_y = env::getEnvVar("DXVK_HUD_OFFSET_Y");
++
++ if (!offset_x.empty())
++ offset_x_float = stof(offset_x);
++
++ if (!offset_y.empty())
++ offset_y_float = stof(offset_y);
+
+ if (hudElements.empty())
+ hudElements = device->config().hud;
+@@ -82,33 +94,45 @@ namespace dxvk::hud {
+ m_renderer.beginFrame(ctx, m_uniformData.surfaceSize);
+ }
+
+-
+ void Hud::renderHudElements(const Rc<DxvkContext>& ctx) {
+- HudPos position = { 8.0f, 24.0f };
++ if(GetAsyncKeyState(VK_F12) & 0x8000)
++ {
++ if (GetTickCount() - lastPress > 500){
++ lastPress = GetTickCount();
++ if (show_hud){
++ show_hud = false;
++ } else {
++ show_hud = true;
++ }
++ }
++ }
+
+- if (m_config.elements.test(HudElement::DxvkVersion)) {
+- m_renderer.drawText(ctx, 16.0f,
+- { position.x, position.y },
+- { 1.0f, 1.0f, 1.0f, 1.0f },
+- "DXVK " DXVK_VERSION);
+- position.y += 24.0f;
+- }
+-
+- if (m_config.elements.test(HudElement::DxvkClientApi)) {
+- m_renderer.drawText(ctx, 16.0f,
+- { position.x, position.y },
+- { 1.0f, 1.0f, 1.0f, 1.0f },
+- m_device->clientApi());
+- position.y += 24.0f;
+- }
+-
+- if (m_config.elements.test(HudElement::DeviceInfo)) {
+- position = m_hudDeviceInfo.render(
+- ctx, m_renderer, position);
+- }
++ HudPos position = { offset_x_float + 8.0f, offset_y_float + 24.0f };
+
+- position = m_hudFramerate.render(ctx, m_renderer, position);
+- position = m_hudStats .render(ctx, m_renderer, position);
++ if (show_hud){
++ if (m_config.elements.test(HudElement::DxvkVersion)) {
++ m_renderer.drawText(ctx, 16.0f,
++ { position.x, position.y },
++ { 1.0f, 1.0f, 1.0f, 1.0f },
++ "DXVK " DXVK_VERSION);
++ position.y += 24.0f;
++ }
++
++ if (m_config.elements.test(HudElement::DxvkClientApi)) {
++ m_renderer.drawText(ctx, 16.0f,
++ { position.x, position.y },
++ { 1.0f, 1.0f, 1.0f, 1.0f },
++ m_device->clientApi());
++ position.y += 24.0f;
++ }
++
++ if (m_config.elements.test(HudElement::DeviceInfo)) {
++ position = m_hudDeviceInfo.render(
++ ctx, m_renderer, position);
++ }
++ position = m_hudFramerate.render(ctx, m_renderer, position);
++ position = m_hudStats .render(ctx, m_renderer, position);
++ }
+ }
+
+
+diff --git a/src/dxvk/hud/dxvk_hud.h b/src/dxvk/hud/dxvk_hud.h
+index 3b702c3b..71779977 100644
+--- a/src/dxvk/hud/dxvk_hud.h
++++ b/src/dxvk/hud/dxvk_hud.h
+@@ -29,6 +29,8 @@ namespace dxvk::hud {
+ class Hud : public RcObject {
+
+ public:
++ static float offset_x_float;
++ static float offset_y_float;
+
+ Hud(
+ const Rc<DxvkDevice>& device,
+diff --git a/src/dxvk/hud/dxvk_hud_config.cpp b/src/dxvk/hud/dxvk_hud_config.cpp
+index fe1745bd..020a394b 100644
+--- a/src/dxvk/hud/dxvk_hud_config.cpp
++++ b/src/dxvk/hud/dxvk_hud_config.cpp
+@@ -16,6 +16,9 @@ namespace dxvk::hud {
+ { "version", HudElement::DxvkVersion },
+ { "api", HudElement::DxvkClientApi },
+ { "compiler", HudElement::CompilerActivity },
++ { "mangogpuload", HudElement::GpuLoad },
++ { "mangocpuload", HudElement::CpuLoad },
++ { "mangocpuload", HudElement::Logging },
+ }};
+
+
+diff --git a/src/dxvk/hud/dxvk_hud_config.h b/src/dxvk/hud/dxvk_hud_config.h
+index 05a1e4b6..5d571663 100644
+--- a/src/dxvk/hud/dxvk_hud_config.h
++++ b/src/dxvk/hud/dxvk_hud_config.h
+@@ -22,6 +22,9 @@ namespace dxvk::hud {
+ DxvkVersion = 8,
+ DxvkClientApi = 9,
+ CompilerActivity = 10,
++ GpuLoad = 11,
++ CpuLoad = 12,
++ Logging = 13,
+ };
+
+ using HudElements = Flags<HudElement>;
+diff --git a/src/dxvk/hud/dxvk_hud_fps.cpp b/src/dxvk/hud/dxvk_hud_fps.cpp
+index f8cb6e7d..84eb2db6 100644
+--- a/src/dxvk/hud/dxvk_hud_fps.cpp
++++ b/src/dxvk/hud/dxvk_hud_fps.cpp
+@@ -1,7 +1,22 @@
+ #include "dxvk_hud_fps.h"
++#include "dxvk_hud_stats.h"
++#include "../dxvk_cpu.h"
++#include <time.h>
+
+ #include <cmath>
+ #include <iomanip>
++using namespace std;
++time_t now_log = time(0);
++tm *log_time = localtime(&now_log);
++fstream f;
++
++struct logData{
++ float fps;
++ float cpu;
++ uint64_t gpu;
++};
++
++std::vector<logData> logArray;
+
+ namespace dxvk::hud {
+
+@@ -9,8 +24,8 @@ namespace dxvk::hud {
+ : m_elements (elements),
+ m_fpsString ("FPS: "),
+ m_prevFpsUpdate(Clock::now()),
+- m_prevFtgUpdate(Clock::now()) {
+-
++ m_prevFtgUpdate(Clock::now()),
++ m_prevLogUpdate(Clock::now()) {
+ }
+
+
+@@ -25,11 +40,48 @@ namespace dxvk::hud {
+ TimePoint now = Clock::now();
+ TimeDiff elapsedFps = std::chrono::duration_cast<TimeDiff>(now - m_prevFpsUpdate);
+ TimeDiff elapsedFtg = std::chrono::duration_cast<TimeDiff>(now - m_prevFtgUpdate);
++ TimeDiff elapsedLog = std::chrono::duration_cast<TimeDiff>(now - m_prevLogUpdate);
+ m_prevFtgUpdate = now;
++
++ if(GetAsyncKeyState(VK_F2) & 0x8000)
++ {
++ elapsedF2 = std::chrono::duration_cast<TimeDiff>(now - m_prevF2Press);
++ if (elapsedF2.count() > UpdateInterval || elapsedF2.count() == 0) {
++ if (mango_logging){
++ m_prevF2Press = now;
++ mango_logging = false;
++ for (size_t i = 0; i < logArray.size(); i++) {
++ f << logArray[i].fps << "," << logArray[i].cpu << "," << logArray[i].gpu << endl;
++ }
++ f.close();
++ logArray.clear();
++ } else {
++ m_prevF2Press = now;
++ now_log = time(0);
++ log_time = localtime(&now_log);
++ mango_logging = true;
++ string date = to_string(log_time->tm_year + 1900) + "-" + to_string(1 + log_time->tm_mon) + "-" + to_string(log_time->tm_mday) + "_" + to_string(1 + log_time->tm_hour) + "-" + to_string(1 + log_time->tm_min) + "-" + to_string(1 + log_time->tm_sec);
++ f.open(logging + "_" + date, f.out | f.app);
++ }
++ }
++ }
++
++ if (elapsedLog.count() >= LogUpdateInterval) {
++ fps = (10'000'000ll * m_frameCount) / elapsedFps.count();
++ if (!logging.empty()){
++ if (mango_logging){
++ logArray.push_back({float(fps / 10 + (float(fps % 10) / 10)), cpuArray[0].value, gpuLoad});
++ }
++ }
++ m_prevLogUpdate = now;
++ }
+
+- // Update FPS string
+ if (elapsedFps.count() >= UpdateInterval) {
+- const int64_t fps = (10'000'000ll * m_frameCount) / elapsedFps.count();
++ // Update FPS string
++ coreCounting();
++ dxvk::thread([this] () { getCpuUsage();});
++ updateCpuStrings();
++ m_cpuUtilizationString = str::format(cpuArray[0].output);
+ m_fpsString = str::format("FPS: ", fps / 10, ".", fps % 10);
+
+ m_prevFpsUpdate = now;
+@@ -46,32 +98,81 @@ namespace dxvk::hud {
+ const Rc<DxvkContext>& context,
+ HudRenderer& renderer,
+ HudPos position) {
++ if (m_elements.test(HudElement::GpuLoad)) {
++ position = this->renderGpuText(
++ context, renderer, position);
++ }
++ if (m_elements.test(HudElement::CpuLoad)) {
++ position = this->renderCpuText(
++ context, renderer, position);
++ }
+ if (m_elements.test(HudElement::Framerate)) {
+ position = this->renderFpsText(
+ context, renderer, position);
+- }
++ }
+
+ if (m_elements.test(HudElement::Frametimes)) {
+ position = this->renderFrametimeGraph(
+ context, renderer, position);
+ }
+
++ if (mango_logging && !logging.empty()) {
++ this->renderLogging(context, renderer,
++ { float(renderer.surfaceSize().width) - 250.0f, float(renderer.surfaceSize().height) - 20.0f });
++ }
++
+ return position;
+ }
+
+
+- HudPos HudFps::renderFpsText(
+- const Rc<DxvkContext>& context,
+- HudRenderer& renderer,
+- HudPos position) {
++
++ HudPos HudFps::renderGpuText(
++ const Rc<DxvkContext>& context,
++ HudRenderer& renderer,
++ HudPos position) {
++ renderer.drawText(context, 16.0f,
++ { position.x, position.y },
++ { 1.0f, 1.0f, 1.0f, 1.0f },
++ m_gpuLoadString);
++
++ return HudPos { position.x, position.y + 24 };
++}
++
++HudPos HudFps::renderCpuText(
++const Rc<DxvkContext>& context,
++ HudRenderer& renderer,
++ HudPos position) {
++renderer.drawText(context, 16.0f,
++ { position.x, position.y },
++ { 1.0f, 1.0f, 1.0f, 1.0f },
++ m_cpuUtilizationString);
++
++return HudPos { position.x, position.y + 24 };
++}
++
++HudPos HudFps::renderFpsText(
++ const Rc<DxvkContext>& context,
++ HudRenderer& renderer,
++ HudPos position) {
+ renderer.drawText(context, 16.0f,
+ { position.x, position.y },
+ { 1.0f, 1.0f, 1.0f, 1.0f },
+ m_fpsString);
++
++ return HudPos { position.x, position.y + 24 };
++ }
+
+- return HudPos { position.x, position.y + 24 };
+- }
+-
++ HudPos HudFps::renderLogging(
++ const Rc<DxvkContext>& context,
++ HudRenderer& renderer,
++ HudPos position) {
++ renderer.drawText(context, 16.0f,
++ { position.x, position.y },
++ { 1.0f, 1.0f, 1.0f, 1.0f },
++ "Currently Logging...");
++
++ return HudPos { position.x, position.y};
++ }
+
+ HudPos HudFps::renderFrametimeGraph(
+ const Rc<DxvkContext>& context,
+diff --git a/src/dxvk/hud/dxvk_hud_fps.h b/src/dxvk/hud/dxvk_hud_fps.h
+index c8c4b984..b1476889 100644
+--- a/src/dxvk/hud/dxvk_hud_fps.h
++++ b/src/dxvk/hud/dxvk_hud_fps.h
+@@ -17,8 +17,9 @@ namespace dxvk::hud {
+ using TimeDiff = std::chrono::microseconds;
+ using TimePoint = typename Clock::time_point;
+
+- constexpr static uint32_t NumDataPoints = 300;
+- constexpr static int64_t UpdateInterval = 500'000;
++ constexpr static uint32_t NumDataPoints = 300;
++ constexpr static int64_t UpdateInterval = 500'000;
++ constexpr static int64_t LogUpdateInterval = 100'000;
+ public:
+
+ HudFps(HudElements elements);
+@@ -36,23 +37,46 @@ namespace dxvk::hud {
+ const HudElements m_elements;
+
+ std::string m_fpsString;
++ bool mango_logging = false;
++ time_t lastPress = time(0);
++ std::string logging = env::getEnvVar("DXVK_LOG_TO_FILE");
++ int64_t fps;
+
+ TimePoint m_prevFpsUpdate;
+ TimePoint m_prevFtgUpdate;
++ TimePoint m_prevLogUpdate;
++ TimePoint m_prevF2Press;
++ TimeDiff elapsedF2;
+ int64_t m_frameCount = 0;
+
+ std::array<float, NumDataPoints> m_dataPoints = {};
+ uint32_t m_dataPointId = 0;
++
++ HudPos renderGpuText(
++ const Rc<DxvkContext>& context,
++ HudRenderer& renderer,
++ HudPos position);
++
++ HudPos renderCpuText(
++ const Rc<DxvkContext>& context,
++ HudRenderer& renderer,
++ HudPos position);
+
+ HudPos renderFpsText(
+ const Rc<DxvkContext>& context,
+ HudRenderer& renderer,
+ HudPos position);
+
++
+ HudPos renderFrametimeGraph(
+ const Rc<DxvkContext>& context,
+ HudRenderer& renderer,
+ HudPos position);
++
++ HudPos renderLogging(
++ const Rc<DxvkContext>& context,
++ HudRenderer& renderer,
++ HudPos position);
+
+ };
+
+diff --git a/src/dxvk/hud/dxvk_hud_stats.cpp b/src/dxvk/hud/dxvk_hud_stats.cpp
+index 995f186b..be8a5eb7 100644
+--- a/src/dxvk/hud/dxvk_hud_stats.cpp
++++ b/src/dxvk/hud/dxvk_hud_stats.cpp
+@@ -1,5 +1,7 @@
+ #include "dxvk_hud_stats.h"
+
++std::string m_gpuLoadString = "GPU: ";
++uint64_t gpuLoad;
+ namespace dxvk::hud {
+
+ HudStats::HudStats(HudElements elements)
+@@ -24,7 +26,7 @@ namespace dxvk::hud {
+
+ // GPU load is a bit more complex than that since
+ // we don't want to update this every frame
+- if (m_elements.test(HudElement::StatGpuLoad))
++ if (m_elements.test(HudElement::GpuLoad) || m_elements.test(HudElement::StatGpuLoad))
+ this->updateGpuLoad();
+ }
+
+@@ -70,8 +72,8 @@ namespace dxvk::hud {
+ uint64_t busyTicks = ticks > m_diffGpuIdleTicks
+ ? uint64_t(ticks - m_diffGpuIdleTicks)
+ : uint64_t(0);
+-
+- m_gpuLoadString = str::format("GPU: ", (100 * busyTicks) / ticks, "%");
++ gpuLoad = 100 * busyTicks / ticks;
++ m_gpuLoadString = str::format("GPU: ", (100 * busyTicks) / ticks, "%");
+ }
+ }
+
+@@ -224,7 +226,8 @@ namespace dxvk::hud {
+ HudElement::StatPipelines,
+ HudElement::StatMemory,
+ HudElement::StatGpuLoad,
+- HudElement::CompilerActivity);
++ HudElement::CompilerActivity,
++ HudElement::GpuLoad);
+ }
+
+ }
+diff --git a/src/dxvk/hud/dxvk_hud_stats.h b/src/dxvk/hud/dxvk_hud_stats.h
+index 227f600c..b7d740f1 100644
+--- a/src/dxvk/hud/dxvk_hud_stats.h
++++ b/src/dxvk/hud/dxvk_hud_stats.h
+@@ -7,6 +7,8 @@
+ #include "dxvk_hud_config.h"
+ #include "dxvk_hud_renderer.h"
+
++extern std::string m_gpuLoadString;
++extern uint64_t gpuLoad;
+ namespace dxvk::hud {
+
+ /**
+@@ -44,8 +46,6 @@ namespace dxvk::hud {
+ uint64_t m_prevGpuIdleTicks = 0;
+ uint64_t m_diffGpuIdleTicks = 0;
+
+- std::string m_gpuLoadString = "GPU: ";
+-
+ void updateGpuLoad();
+
+ HudPos printDrawCallStats(
diff --git a/extraopts.patch b/extraopts.patch
new file mode 100644
index 000000000000..43a8fd6d10ec
--- /dev/null
+++ b/extraopts.patch
@@ -0,0 +1,70 @@
+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]
+diff --git a/build-wine32.txt b/build-wine32.txt
+index ba8d34a8..ecec2eb4 100644
+--- a/build-wine32.txt
++++ b/build-wine32.txt
+@@ -7,9 +7,9 @@ strip = 'strip'
+ [properties]
+ needs_exe_wrapper = true
+
+-c_args=['-m32', '-msse', '-msse2', '-fvisibility=hidden']
+-cpp_args=['-m32', '-msse', '-msse2', '-fvisibility=hidden', '-fvisibility-inlines-hidden', '-D__WIDL_objidl_generated_name_0000000C=']
+-cpp_link_args=['-m32', '-mwindows']
++c_args=['-m32', '-msse', '-msse2', '-fvisibility=hidden', @CARGS@]
++cpp_args=['-m32', '-msse', '-msse2', '-fvisibility=hidden', '-fvisibility-inlines-hidden', '-D__WIDL_objidl_generated_name_0000000C=', @CARGS@]
++cpp_link_args=['-m32', '-mwindows', @LDARGS@]
+
+ [host_machine]
+ system = 'linux'
+diff --git a/build-wine64.txt b/build-wine64.txt
+index b3e028bb..fb9d98fd 100644
+--- a/build-wine64.txt
++++ b/build-wine64.txt
+@@ -7,9 +7,9 @@ strip = 'strip'
+ [properties]
+ needs_exe_wrapper = true
+
+-c_args=['-m64', '-fvisibility=hidden']
+-cpp_args=['-m64', '-fvisibility=hidden', '-fvisibility-inlines-hidden', '-D__WIDL_objidl_generated_name_0000000C=']
+-cpp_link_args=['-m64', '-mwindows']
++c_args=['-m64', '-fvisibility=hidden', @CARGS@]
++cpp_args=['-m64', '-fvisibility=hidden', '-fvisibility-inlines-hidden', '-D__WIDL_objidl_generated_name_0000000C=', @CARGS@]
++cpp_link_args=['-m64', '-mwindows', @LDARGS@]
+
+ [host_machine]
+ system = 'linux'