summaryrefslogtreecommitdiffstats
path: root/src/libs/dxvk-native-1.9.2a/src/util
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/libs/dxvk-native-1.9.2a/src/util
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libs/dxvk-native-1.9.2a/src/util')
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/com/com_guid.cpp28
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/com/com_guid.h8
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/com/com_include.h17
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/com/com_object.h119
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/com/com_pointer.h146
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/com/com_private_data.cpp171
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/com/com_private_data.h115
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/config/config.cpp723
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/config/config.h152
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/log/log.cpp146
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/log/log.h71
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/log/log_debug.cpp11
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/log/log_debug.h49
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/meson.build49
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/platform/thread_native.cpp21
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/platform/util_env_linux.cpp44
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/platform/util_env_win32.cpp38
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/platform/util_luid_linux.cpp13
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/platform/util_luid_win32.cpp34
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/platform/util_string_linux.cpp19
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/platform/util_string_win32.cpp43
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/rc/util_rc.h36
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/rc/util_rc_ptr.h124
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1.c170
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1.h53
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1_util.cpp48
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1_util.h63
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/sync/sync_recursive.cpp37
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/sync/sync_recursive.h32
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/sync/sync_signal.h160
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/sync/sync_spinlock.h69
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/sync/sync_ticketlock.h40
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/thread.h351
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_bit.h349
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_enum.h7
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_env.cpp71
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_env.h73
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_error.h31
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_flags.h110
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_fps_limiter.cpp175
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_fps_limiter.h89
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_gdi.cpp33
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_gdi.h32
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_lazy.h40
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_likely.h9
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_luid.h15
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_math.h34
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_matrix.cpp232
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_matrix.h85
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_monitor.cpp51
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_monitor.h47
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_ratio.h88
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_small_vector.h133
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_string.h43
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_time.h50
-rw-r--r--src/libs/dxvk-native-1.9.2a/src/util/util_vector.h161
56 files changed, 5158 insertions, 0 deletions
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/com/com_guid.cpp b/src/libs/dxvk-native-1.9.2a/src/util/com/com_guid.cpp
new file mode 100644
index 00000000..329d1848
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/com/com_guid.cpp
@@ -0,0 +1,28 @@
+#include "com_guid.h"
+
+#include "../../d3d11/d3d11_interfaces.h"
+
+#include "../../dxgi/dxgi_interfaces.h"
+
+std::ostream& operator << (std::ostream& os, REFIID guid) {
+ os << std::hex << std::setfill('0')
+ << std::setw(8) << guid.Data1 << '-';
+
+ os << std::hex << std::setfill('0')
+ << std::setw(4) << guid.Data2 << '-';
+
+ os << std::hex << std::setfill('0')
+ << std::setw(4) << guid.Data3 << '-';
+
+ os << std::hex << std::setfill('0')
+ << std::setw(2) << static_cast<short>(guid.Data4[0])
+ << std::setw(2) << static_cast<short>(guid.Data4[1])
+ << '-'
+ << std::setw(2) << static_cast<short>(guid.Data4[2])
+ << std::setw(2) << static_cast<short>(guid.Data4[3])
+ << std::setw(2) << static_cast<short>(guid.Data4[4])
+ << std::setw(2) << static_cast<short>(guid.Data4[5])
+ << std::setw(2) << static_cast<short>(guid.Data4[6])
+ << std::setw(2) << static_cast<short>(guid.Data4[7]);
+ return os;
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/com/com_guid.h b/src/libs/dxvk-native-1.9.2a/src/util/com/com_guid.h
new file mode 100644
index 00000000..9a69fc93
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/com/com_guid.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <ostream>
+#include <iomanip>
+
+#include "com_include.h"
+
+std::ostream& operator << (std::ostream& os, REFIID guid);
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/com/com_include.h b/src/libs/dxvk-native-1.9.2a/src/util/com/com_include.h
new file mode 100644
index 00000000..3c119309
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/com/com_include.h
@@ -0,0 +1,17 @@
+#pragma once
+
+// GCC complains about the COM interfaces
+// not having virtual destructors
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif // __GNUC__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <unknwn.h>
+
+// GCC: -std options disable certain keywords
+// https://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html
+#if defined(__WINE__) && !defined(typeof)
+#define typeof __typeof
+#endif
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/com/com_object.h b/src/libs/dxvk-native-1.9.2a/src/util/com/com_object.h
new file mode 100644
index 00000000..75188d5c
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/com/com_object.h
@@ -0,0 +1,119 @@
+#pragma once
+
+#include <atomic>
+
+#include "com_include.h"
+
+#include "../util_likely.h"
+
+namespace dxvk {
+
+ template<typename T>
+ class NoWrapper : public T {
+
+ public:
+
+ virtual ~NoWrapper() { }
+
+ };
+
+ /**
+ * \brief Reference-counted COM object
+ *
+ * This can serve as a templated base class for most
+ * COM objects. It implements AddRef and Release from
+ * \c IUnknown, and provides methods to increment and
+ * decrement private references which are not visible
+ * to the application.
+ *
+ * Having two reference counters is sadly necessary
+ * in order to not break games which steal internal
+ * references if the refefence count of an object is
+ + greater than they expect. DXVK sometimes requires
+ * holding on to objects which the application wants
+ * to delete.
+ */
+ template<typename Base>
+ class ComObject : public Base {
+
+ public:
+
+ virtual ~ComObject() { }
+
+ ULONG STDMETHODCALLTYPE AddRef() {
+ uint32_t refCount = m_refCount++;
+ if (unlikely(!refCount))
+ AddRefPrivate();
+ return refCount + 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release() {
+ uint32_t refCount = --m_refCount;
+ if (unlikely(!refCount))
+ ReleasePrivate();
+ return refCount;
+ }
+
+
+ void AddRefPrivate() {
+ ++m_refPrivate;
+ }
+
+
+ void ReleasePrivate() {
+ uint32_t refPrivate = --m_refPrivate;
+ if (unlikely(!refPrivate)) {
+ m_refPrivate += 0x80000000;
+ delete this;
+ }
+ }
+
+ ULONG GetPrivateRefCount() {
+ return m_refPrivate.load();
+ }
+
+ protected:
+
+ std::atomic<uint32_t> m_refCount = { 0ul };
+ std::atomic<uint32_t> m_refPrivate = { 0ul };
+
+ };
+
+ /**
+ * \brief Clamped, reference-counted COM object
+ *
+ * This version of ComObject ensures that the reference
+ * count does not wrap around if a release happens at zero.
+ * eg. [m_refCount = 0]
+ * Release()
+ * [m_refCount = 0]
+ * This is a notable quirk of D3D9's COM implementation
+ * and is relied upon by some games.
+ */
+ template<typename Base>
+ class ComObjectClamp : public ComObject<Base> {
+
+ public:
+
+ ULONG STDMETHODCALLTYPE Release() {
+ ULONG refCount = this->m_refCount;
+ if (likely(refCount != 0ul)) {
+ this->m_refCount--;
+ refCount--;
+
+ if (refCount == 0ul)
+ this->ReleasePrivate();
+ }
+
+ return refCount;
+ }
+
+ };
+
+ template<typename T>
+ inline void InitReturnPtr(T** ptr) {
+ if (ptr != nullptr)
+ *ptr = nullptr;
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/com/com_pointer.h b/src/libs/dxvk-native-1.9.2a/src/util/com/com_pointer.h
new file mode 100644
index 00000000..c81fae24
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/com/com_pointer.h
@@ -0,0 +1,146 @@
+#pragma once
+
+#include "com_include.h"
+
+namespace dxvk {
+
+ /**
+ * \brief Increment public ref count
+ *
+ * If the pointer is not \c nullptr, this
+ * calls \c AddRef for the given object.
+ * \returns Pointer to the object
+ */
+ template<typename T>
+ T* ref(T* object) {
+ if (object != nullptr)
+ object->AddRef();
+ return object;
+ }
+
+
+ /**
+ * \brief Ref count methods for public references
+ */
+ template<typename T, bool Public>
+ struct ComRef_ {
+ static void incRef(T* ptr) { ptr->AddRef(); }
+ static void decRef(T* ptr) { ptr->Release(); }
+ };
+
+
+ /**
+ * \brief Ref count methods for private references
+ */
+ template<typename T>
+ struct ComRef_<T, false> {
+ static void incRef(T* ptr) { ptr->AddRefPrivate(); }
+ static void decRef(T* ptr) { ptr->ReleasePrivate(); }
+ };
+
+
+ /**
+ * \brief COM pointer
+ *
+ * Implements automatic reference
+ * counting for COM objects.
+ */
+ template<typename T, bool Public = true>
+ class Com {
+ using ComRef = ComRef_<T, Public>;
+ public:
+
+ Com() { }
+ Com(std::nullptr_t) { }
+ Com(T* object)
+ : m_ptr(object) {
+ this->incRef();
+ }
+
+ Com(const Com& other)
+ : m_ptr(other.m_ptr) {
+ this->incRef();
+ }
+
+ Com(Com&& other)
+ : m_ptr(other.m_ptr) {
+ other.m_ptr = nullptr;
+ }
+
+ Com& operator = (T* object) {
+ this->decRef();
+ m_ptr = object;
+ this->incRef();
+ return *this;
+ }
+
+ Com& operator = (const Com& other) {
+ other.incRef();
+ this->decRef();
+ m_ptr = other.m_ptr;
+ return *this;
+ }
+
+ Com& operator = (Com&& other) {
+ this->decRef();
+ this->m_ptr = other.m_ptr;
+ other.m_ptr = nullptr;
+ return *this;
+ }
+
+ Com& operator = (std::nullptr_t) {
+ this->decRef();
+ m_ptr = nullptr;
+ return *this;
+ }
+
+ ~Com() {
+ this->decRef();
+ }
+
+ T* operator -> () const {
+ return m_ptr;
+ }
+
+ T** operator & () { return &m_ptr; }
+ T* const* operator & () const { return &m_ptr; }
+
+ template<bool Public_>
+ bool operator == (const Com<T, Public_>& other) const { return m_ptr == other.m_ptr; }
+ template<bool Public_>
+ bool operator != (const Com<T, Public_>& other) const { return m_ptr != other.m_ptr; }
+
+ bool operator == (const T* other) const { return m_ptr == other; }
+ bool operator != (const T* other) const { return m_ptr != other; }
+
+ bool operator == (std::nullptr_t) const { return m_ptr == nullptr; }
+ bool operator != (std::nullptr_t) const { return m_ptr != nullptr; }
+
+ T* ref() const {
+ return dxvk::ref(m_ptr);
+ }
+
+ T* ptr() const {
+ return m_ptr;
+ }
+
+ Com<T, true> pubRef() const { return m_ptr; }
+ Com<T, false> prvRef() const { return m_ptr; }
+
+ private:
+
+ T* m_ptr = nullptr;
+
+ void incRef() const {
+ if (m_ptr != nullptr)
+ ComRef::incRef(m_ptr);
+ }
+
+ void decRef() const {
+ if (m_ptr != nullptr)
+ ComRef::decRef(m_ptr);
+ }
+
+ };
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/com/com_private_data.cpp b/src/libs/dxvk-native-1.9.2a/src/util/com/com_private_data.cpp
new file mode 100644
index 00000000..d27a4103
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/com/com_private_data.cpp
@@ -0,0 +1,171 @@
+#include <cmath>
+#include <cstring>
+#include <cstdlib>
+
+#include "com_private_data.h"
+
+namespace dxvk {
+
+ ComPrivateDataEntry::ComPrivateDataEntry() { }
+ ComPrivateDataEntry::ComPrivateDataEntry(
+ REFGUID guid,
+ UINT size,
+ const void* data)
+ : m_guid(guid),
+ m_type(ComPrivateDataType::Data),
+ m_size(size),
+ m_data(std::malloc(size)) {
+ std::memcpy(m_data, data, size);
+ }
+
+
+ ComPrivateDataEntry::ComPrivateDataEntry(
+ REFGUID guid,
+ const IUnknown* iface)
+ : m_guid (guid),
+ m_type(ComPrivateDataType::Iface),
+ m_iface (const_cast<IUnknown*>(iface)) {
+ if (m_iface)
+ m_iface->AddRef();
+ }
+
+
+ ComPrivateDataEntry::~ComPrivateDataEntry() {
+ this->destroy();
+ }
+
+
+ ComPrivateDataEntry::ComPrivateDataEntry(ComPrivateDataEntry&& other)
+ : m_guid (other.m_guid),
+ m_type (other.m_type),
+ m_size (other.m_size),
+ m_data (other.m_data),
+ m_iface (other.m_iface) {
+ other.m_guid = __uuidof(IUnknown);
+ other.m_type = ComPrivateDataType::None;
+ other.m_size = 0;
+ other.m_data = nullptr;
+ other.m_iface = nullptr;
+ }
+
+
+ ComPrivateDataEntry& ComPrivateDataEntry::operator = (ComPrivateDataEntry&& other) {
+ this->destroy();
+ this->m_guid = other.m_guid;
+ this->m_type = other.m_type;
+ this->m_size = other.m_size;
+ this->m_data = other.m_data;
+ this->m_iface = other.m_iface;
+
+ other.m_guid = __uuidof(IUnknown);
+ other.m_type = ComPrivateDataType::None;
+ other.m_size = 0;
+ other.m_data = nullptr;
+ other.m_iface = nullptr;
+ return *this;
+ }
+
+
+ HRESULT ComPrivateDataEntry::get(UINT& size, void* data) const {
+ UINT minSize = 0;
+
+ if (m_type == ComPrivateDataType::Iface) minSize = sizeof(IUnknown*);
+ if (m_type == ComPrivateDataType::Data) minSize = m_size;
+
+ if (!data) {
+ size = minSize;
+ return S_OK;
+ }
+
+ HRESULT result = size < minSize
+ ? DXGI_ERROR_MORE_DATA
+ : S_OK;
+
+ if (size >= minSize) {
+ if (m_type == ComPrivateDataType::Iface) {
+ if (m_iface)
+ m_iface->AddRef();
+ std::memcpy(data, &m_iface, minSize);
+ } else {
+ std::memcpy(data, m_data, minSize);
+ }
+ }
+
+ size = minSize;
+ return result;
+ }
+
+
+ void ComPrivateDataEntry::destroy() {
+ if (m_data)
+ std::free(m_data);
+ if (m_iface)
+ m_iface->Release();
+ }
+
+
+ HRESULT ComPrivateData::setData(
+ REFGUID guid,
+ UINT size,
+ const void* data) {
+ if (!data) {
+ for (auto it = m_entries.begin(); it != m_entries.end(); ++it) {
+ if (it->hasGuid(guid)) {
+ m_entries.erase(it);
+ return S_OK;
+ }
+ }
+ return S_FALSE;
+ }
+ this->insertEntry(ComPrivateDataEntry(guid, size, data));
+ return S_OK;
+ }
+
+
+ HRESULT ComPrivateData::setInterface(
+ REFGUID guid,
+ const IUnknown* iface) {
+ this->insertEntry(ComPrivateDataEntry(guid, iface));
+ return S_OK;
+ }
+
+
+ HRESULT ComPrivateData::getData(
+ REFGUID guid,
+ UINT* size,
+ void* data) {
+ if (!size)
+ return E_INVALIDARG;
+
+ auto entry = this->findEntry(guid);
+
+ if (!entry) {
+ *size = 0;
+ return DXGI_ERROR_NOT_FOUND;
+ }
+
+ return entry->get(*size, data);
+ }
+
+
+ ComPrivateDataEntry* ComPrivateData::findEntry(REFGUID guid) {
+ for (ComPrivateDataEntry& e : m_entries) {
+ if (e.hasGuid(guid))
+ return &e;
+ }
+
+ return nullptr;
+ }
+
+
+ void ComPrivateData::insertEntry(ComPrivateDataEntry&& entry) {
+ ComPrivateDataEntry srcEntry = std::move(entry);
+ ComPrivateDataEntry* dstEntry = this->findEntry(srcEntry.guid());
+
+ if (dstEntry)
+ *dstEntry = std::move(srcEntry);
+ else
+ m_entries.push_back(std::move(srcEntry));
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/com/com_private_data.h b/src/libs/dxvk-native-1.9.2a/src/util/com/com_private_data.h
new file mode 100644
index 00000000..0673f8e2
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/com/com_private_data.h
@@ -0,0 +1,115 @@
+#pragma once
+
+#include <vector>
+
+#include "com_include.h"
+
+namespace dxvk {
+
+ /**
+ * \brief COM private data entry type
+ */
+ enum ComPrivateDataType {
+ None,
+ Data,
+ Iface,
+ };
+
+ /**
+ * \brief Data entry for private storage
+ * Stores a single private storage item.
+ */
+ class ComPrivateDataEntry {
+
+ public:
+
+ ComPrivateDataEntry();
+ ComPrivateDataEntry(
+ REFGUID guid,
+ UINT size,
+ const void* data);
+ ComPrivateDataEntry(
+ REFGUID guid,
+ const IUnknown* iface);
+ ~ComPrivateDataEntry();
+
+ ComPrivateDataEntry (ComPrivateDataEntry&& other);
+ ComPrivateDataEntry& operator = (ComPrivateDataEntry&& other);
+
+ /**
+ * \brief The entry's GUID
+ * \returns The GUID
+ */
+ REFGUID guid() const {
+ return m_guid;
+ }
+
+ /**
+ * \brief Checks whether the GUID matches another one
+ *
+ * GUIDs are used to identify private data entries.
+ * \param [in] guid The GUID to compare to
+ * \returns \c true if this entry holds the same GUID
+ */
+ bool hasGuid(REFGUID guid) const {
+ return m_guid == guid;
+ }
+
+ /**
+ * \brief Retrieves stored data
+ *
+ * \param [in,out] size Destination buffer size
+ * \param [in] data Appliaction-provided buffer
+ * \returns \c S_OK on success, or \c DXGI_ERROR_MORE_DATA
+ * if the destination buffer is too small
+ */
+ HRESULT get(UINT& size, void* data) const;
+
+ private:
+
+ GUID m_guid = __uuidof(IUnknown);
+ ComPrivateDataType m_type = ComPrivateDataType::None;
+ UINT m_size = 0;
+ void* m_data = nullptr;
+ IUnknown* m_iface = nullptr;
+
+ void destroy();
+
+ };
+
+
+ /**
+ * \brief Private storage for DXGI objects
+ *
+ * Provides storage for application-defined
+ * byte arrays or COM interfaces that can be
+ * retrieved using GUIDs.
+ */
+ class ComPrivateData {
+
+ public:
+
+ HRESULT setData(
+ REFGUID guid,
+ UINT size,
+ const void* data);
+
+ HRESULT setInterface(
+ REFGUID guid,
+ const IUnknown* iface);
+
+ HRESULT getData(
+ REFGUID guid,
+ UINT* size,
+ void* data);
+
+ private:
+
+ std::vector<ComPrivateDataEntry> m_entries;
+
+ ComPrivateDataEntry* findEntry(REFGUID guid);
+ void insertEntry(ComPrivateDataEntry&& entry);
+
+ };
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/config/config.cpp b/src/libs/dxvk-native-1.9.2a/src/util/config/config.cpp
new file mode 100644
index 00000000..f5c940d1
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/config/config.cpp
@@ -0,0 +1,723 @@
+#include <array>
+#include <fstream>
+#include <sstream>
+#include <iostream>
+#include <regex>
+#include <utility>
+
+#include "config.h"
+
+#include "../log/log.h"
+
+#include "../util_env.h"
+
+namespace dxvk {
+
+ const static std::vector<std::pair<const char*, Config>> g_appDefaults = {{
+ /* Assassin's Creed Syndicate: amdags issues */
+ { R"(\\ACS\.exe$)", {{
+ { "dxgi.customVendorId", "10de" },
+ }} },
+ /* Dissidia Final Fantasy NT Free Edition */
+ { R"(\\dffnt\.exe$)", {{
+ { "dxgi.deferSurfaceCreation", "True" },
+ }} },
+ /* Elite Dangerous: Compiles weird shaders *
+ * when running on AMD hardware */
+ { R"(\\EliteDangerous64\.exe$)", {{
+ { "dxgi.customVendorId", "10de" },
+ }} },
+ /* The Vanishing of Ethan Carter Redux */
+ { R"(\\EthanCarter-Win64-Shipping\.exe$)", {{
+ { "dxgi.customVendorId", "10de" },
+ }} },
+ /* The Evil Within: Submits command lists *
+ * multiple times */
+ { R"(\\EvilWithin(Demo)?\.exe$)", {{
+ { "d3d11.dcSingleUseMode", "False" },
+ }} },
+ /* Far Cry 3: Assumes clear(0.5) on an UNORM *
+ * format to result in 128 on AMD and 127 on *
+ * Nvidia. We assume that the Vulkan drivers *
+ * match the clear behaviour of D3D11. */
+ { R"(\\(farcry3|fc3_blooddragon)_d3d11\.exe$)", {{
+ { "dxgi.nvapiHack", "False" },
+ }} },
+ /* Far Cry 4: Same as Far Cry 3 */
+ { R"(\\FarCry4\.exe$)", {{
+ { "dxgi.nvapiHack", "False" },
+ }} },
+ /* Frostpunk: Renders one frame with D3D9 *
+ * after creating the DXGI swap chain */
+ { R"(\\Frostpunk\.exe$)", {{
+ { "dxgi.deferSurfaceCreation", "True" },
+ }} },
+ /* Nioh: See Frostpunk, apparently? */
+ { R"(\\nioh\.exe$)", {{
+ { "dxgi.deferSurfaceCreation", "True" },
+ }} },
+ /* Quantum Break: Mever initializes shared *
+ * memory in one of its compute shaders */
+ { R"(\\QuantumBreak\.exe$)", {{
+ { "d3d11.zeroInitWorkgroupMemory", "True" },
+ }} },
+ /* Anno 2205: Random crashes with state cache */
+ { R"(\\anno2205\.exe$)", {{
+ { "dxvk.enableStateCache", "False" },
+ }} },
+ /* Fifa '19+: Binds typed buffer SRV to shader *
+ * that expects raw/structured buffer SRV */
+ { R"(\\FIFA(19|[2-9][0-9])(_demo)?\.exe$)", {{
+ { "dxvk.useRawSsbo", "True" },
+ }} },
+ /* Resident Evil 2/3: Ignore WaW hazards */
+ { R"(\\re(2|3|3demo)\.exe$)", {{
+ { "d3d11.relaxedBarriers", "True" },
+ }} },
+ /* Devil May Cry 5 */
+ { R"(\\DevilMayCry5\.exe$)", {{
+ { "d3d11.relaxedBarriers", "True" },
+ }} },
+ /* Call of Duty WW2 */
+ { R"(\\s2_sp64_ship\.exe$)", {{
+ { "dxgi.nvapiHack", "False" },
+ }} },
+ /* Need for Speed 2015 */
+ { R"(\\NFS16\.exe$)", {{
+ { "dxgi.nvapiHack", "False" },
+ }} },
+ /* Mass Effect Andromeda */
+ { R"(\\MassEffectAndromeda\.exe$)", {{
+ { "dxgi.nvapiHack", "False" },
+ }} },
+ /* Mirror`s Edge Catalyst: Crashes on AMD */
+ { R"(\\MirrorsEdgeCatalyst(Trial)?\.exe$)", {{
+ { "dxgi.customVendorId", "10de" },
+ }} },
+ /* Star Wars Battlefront (2015) */
+ { R"(\\starwarsbattlefront(trial)?\.exe$)", {{
+ { "dxgi.nvapiHack", "False" },
+ }} },
+ /* Dark Souls Remastered */
+ { R"(\\DarkSoulsRemastered\.exe$)", {{
+ { "d3d11.constantBufferRangeCheck", "True" },
+ }} },
+ /* Grim Dawn */
+ { R"(\\Grim Dawn\.exe$)", {{
+ { "d3d11.constantBufferRangeCheck", "True" },
+ }} },
+ /* NieR:Automata */
+ { R"(\\NieRAutomata\.exe$)", {{
+ { "d3d11.constantBufferRangeCheck", "True" },
+ }} },
+ /* NieR Replicant */
+ { R"(\\NieR Replicant ver\.1\.22474487139\.exe)", {{
+ { "dxgi.syncInterval", "1" },
+ { "dxgi.maxFrameRate", "60" },
+ }} },
+ /* SteamVR performance test */
+ { R"(\\vr\.exe$)", {{
+ { "d3d11.dcSingleUseMode", "False" },
+ }} },
+ /* Hitman 2 and 3 - requires AGS library */
+ { R"(\\HITMAN(2|3)\.exe$)", {{
+ { "dxgi.customVendorId", "10de" },
+ }} },
+ /* Modern Warfare Remastered */
+ { R"(\\h1_[ms]p64_ship\.exe$)", {{
+ { "dxgi.customVendorId", "10de" },
+ }} },
+ /* Titan Quest */
+ { R"(\\TQ\.exe$)", {{
+ { "d3d11.constantBufferRangeCheck", "True" },
+ }} },
+ /* Saints Row IV */
+ { R"(\\SaintsRowIV\.exe$)", {{
+ { "d3d11.constantBufferRangeCheck", "True" },
+ }} },
+ /* Saints Row: The Third */
+ { R"(\\SaintsRowTheThird_DX11\.exe$)", {{
+ { "d3d11.constantBufferRangeCheck", "True" },
+ }} },
+ /* Metal Gear Solid 5 */
+ { R"(\\mgsvtpp\.exe$)", {{
+ { "dxvk.enableOpenVR", "False" },
+ }} },
+ /* Raft */
+ { R"(\\Raft\.exe$)", {{
+ { "dxvk.enableOpenVR", "False" },
+ }} },
+ /* Crysis 3 - slower if it notices AMD card *
+ * Apitrace mode helps massively in cpu bound *
+ * game parts */
+ { R"(\\Crysis3\.exe$)", {{
+ { "dxgi.customVendorId", "10de" },
+ { "d3d11.apitraceMode", "True" },
+ }} },
+ /* Crysis 3 Remastered *
+ * Apitrace mode helps massively in cpu bound *
+ * game parts */
+ { R"(\\Crysis3Remastered\.exe$)", {{
+ { "d3d11.apitraceMode", "True" },
+ }} },
+ /* Atelier series - games try to render video *
+ * with a D3D9 swap chain over the DXGI swap *
+ * chain, which breaks D3D11 presentation */
+ { R"(\\Atelier_(Ayesha|Escha_and_Logy|Shallie)(_EN)?\.exe$)", {{
+ { "d3d9.deferSurfaceCreation", "True" },
+ }} },
+ /* Atelier Rorona/Totori/Meruru */
+ { R"(\\A(11R|12V|13V)_x64_Release(_en)?\.exe$)", {{
+ { "d3d9.deferSurfaceCreation", "True" },
+ }} },
+ /* Just how many of these games are there? */
+ { R"(\\Atelier_(Lulua|Lydie_and_Suelle|Ryza(_2)?)\.exe$)", {{
+ { "d3d9.deferSurfaceCreation", "True" },
+ }} },
+ /* ... */
+ { R"(\\Atelier_(Lydie_and_Suelle|Firis|Sophie)_DX\.exe$)", {{
+ { "d3d9.deferSurfaceCreation", "True" },
+ }} },
+ /* Fairy Tail */
+ { R"(\\FAIRY_TAIL\.exe$)", {{
+ { "d3d9.deferSurfaceCreation", "True" },
+ }} },
+ /* Nights of Azure */
+ { R"(\\CNN\.exe$)", {{
+ { "d3d9.deferSurfaceCreation", "True" },
+ }} },
+ /* Star Wars Battlefront II: amdags issues */
+ { R"(\\starwarsbattlefrontii\.exe$)", {{
+ { "dxgi.customVendorId", "10de" },
+ }} },
+ /* F1 games - do not synchronize TGSM access *
+ * in a compute shader, causing artifacts */
+ { R"(\\F1_20(1[89]|[2-9][0-9])\.exe$)", {{
+ { "d3d11.forceTgsmBarriers", "True" },
+ }} },
+ /* Subnautica */
+ { R"(\\Subnautica\.exe$)", {{
+ { "dxvk.enableOpenVR", "False" },
+ }} },
+ /* Blue Reflection */
+ { R"(\\BLUE_REFLECTION\.exe$)", {{
+ { "d3d11.constantBufferRangeCheck", "True" },
+ }} },
+ /* Secret World Legends */
+ { R"(\\SecretWorldLegendsDX11\.exe$)", {{
+ { "d3d11.constantBufferRangeCheck", "True" },
+ }} },
+ /* Darksiders Warmastered - apparently reads *
+ * from write-only mapped buffers */
+ { R"(\\darksiders1\.exe$)", {{
+ { "d3d11.apitraceMode", "True" },
+ }} },
+ /* Monster Hunter World */
+ { R"(\\MonsterHunterWorld\.exe$)", {{
+ { "d3d11.apitraceMode", "True" },
+ }} },
+ /* Kingdome Come: Deliverance */
+ { R"(\\KingdomCome\.exe$)", {{
+ { "d3d11.apitraceMode", "True" },
+ }} },
+ /* Homefront: The Revolution */
+ { R"(\\Homefront2_Release\.exe$)", {{
+ { "d3d11.apitraceMode", "True" },
+ }} },
+ /* Sniper Ghost Warrior Contracts */
+ { R"(\\SGWContracts\.exe$)", {{
+ { "d3d11.apitraceMode", "True" },
+ }} },
+ /* Shadow of the Tomb Raider - invariant *
+ * position breaks character rendering on NV */
+ { R"(\\SOTTR\.exe$)", {{
+ { "d3d11.invariantPosition", "False" },
+ { "d3d11.floatControls", "False" },
+ }} },
+ /* Nioh 2 */
+ { R"(\\nioh2\.exe$)", {{
+ { "dxgi.deferSurfaceCreation", "True" },
+ }} },
+ /* DIRT 5 - uses amd_ags_x64.dll when it *
+ * detects an AMD GPU */
+ { R"(\\DIRT5\.exe$)", {{
+ { "dxgi.customVendorId", "10de" },
+ }} },
+ /* Crazy Machines 3 - crashes on long device *
+ * descriptions */
+ { R"(\\cm3\.exe$)", {{
+ { "dxgi.customDeviceDesc", "DXVK Adapter" },
+ }} },
+ /* World of Final Fantasy: Broken and useless *
+ * use of 4x MSAA throughout the renderer */
+ { R"(\\WOFF\.exe$)", {{
+ { "d3d11.disableMsaa", "True" },
+ }} },
+
+ /**********************************************/
+ /* D3D9 GAMES */
+ /**********************************************/
+
+ /* A Hat in Time */
+ { R"(\\HatinTimeGame\.exe$)", {{
+ { "d3d9.strictPow", "False" },
+ { "d3d9.lenientClear", "True" },
+ }} },
+ /* Anarchy Online */
+ { R"(\\anarchyonline\.exe$)", {{
+ { "d3d9.memoryTrackTest", "True" },
+ }} },
+ /* Borderlands 2 and The Pre Sequel! */
+ { R"(\\Borderlands(2|PreSequel)\.exe$)", {{
+ { "d3d9.lenientClear", "True" },
+ { "d3d9.supportDFFormats", "False" },
+ }} },
+ /* Borderlands */
+ { R"(\\Borderlands\.exe$)", {{
+ { "d3d9.lenientClear", "True" },
+ }} },
+ /* Gothic 3 */
+ { R"(\\Gothic(3|3Final| III Forsaken Gods)\.exe$)", {{
+ { "d3d9.supportDFFormats", "False" },
+ }} },
+ /* Risen */
+ { R"(\\Risen[23]?\.exe$)", {{
+ { "d3d9.invariantPosition", "True" },
+ }} },
+ /* Sonic Adventure 2 */
+ { R"(\\Sonic Adventure 2\\(launcher|sonic2app)\.exe$)", {{
+ { "d3d9.floatEmulation", "False" },
+ }} },
+ /* The Sims 2,
+ Body Shop,
+ The Sims Life Stories,
+ The Sims Pet Stories,
+ and The Sims Castaway Stories */
+ { R"(\\(Sims2.*|TS2BodyShop|SimsLS|SimsPS|SimsCS)\.exe$)", {{
+ { "d3d9.customVendorId", "10de" },
+ { "d3d9.customDeviceId", "0091" },
+ { "d3d9.customDeviceDesc", "GeForce 7800 GTX" },
+ { "d3d9.disableA8RT", "True" },
+ { "d3d9.supportX4R4G4B4", "False" },
+ { "d3d9.maxAvailableMemory", "2048" },
+ { "d3d9.memoryTrackTest", "True" },
+ // The Sims 2 will try to upload 1024 constants
+ // every frame otherwise, which it never uses
+ // causing a massive discard + upload.
+ { "d3d9.swvpFloatCount", "384" },
+ { "d3d9.swvpIntCount", "16" },
+ { "d3d9.swvpBoolCount", "16" },
+ }} },
+ /* Dead Space uses the a NULL render target instead
+ of a 1x1 one if DF24 is NOT supported */
+ { R"(\\Dead Space\.exe$)", {{
+ { "d3d9.supportDFFormats", "False" },
+ }} },
+ /* Halo 2 */
+ { R"(\\halo2\.exe$)", {{
+ { "d3d9.invariantPosition", "True" },
+ }} },
+ /* Halo CE/HaloPC */
+ { R"(\\halo(ce)?\.exe$)", {{
+ { "d3d9.invariantPosition", "True" },
+ // Game enables minor decal layering fixes
+ // specifically when it detects AMD.
+ // Avoids chip being detected as unsupported
+ // when on intel. Avoids possible path towards
+ // invalid texture addressing methods.
+ { "d3d9.customVendorId", "1002" },
+ // Avoids card not recognized error.
+ // Keeps game's rendering methods consistent
+ // for optimal compatibility.
+ { "d3d9.customDeviceId", "4172" },
+ // The game uses incorrect sampler types in
+ // the shaders for glass rendering which
+ // breaks it on native + us if we don't
+ // spec-constantly chose the sampler type
+ // automagically.
+ { "d3d9.forceSamplerTypeSpecConstants", "True" },
+ }} },
+ /* Counter Strike: Global Offensive
+ Needs NVAPI to avoid a forced AO + Smoke
+ exploit so we must force AMD vendor ID. */
+ { R"(\\csgo\.exe$)", {{
+ { "d3d9.customVendorId", "1002" },
+ }} },
+ /* Vampire - The Masquerade Bloodlines */
+ { R"(\\vampire\.exe$)", {{
+ { "d3d9.deferSurfaceCreation", "True" },
+ { "d3d9.memoryTrackTest", "True" },
+ { "d3d9.maxAvailableMemory", "1024" },
+ }} },
+ /* Senran Kagura Shinovi Versus */
+ { R"(\\SKShinoviVersus\.exe$)", {{
+ { "d3d9.forceAspectRatio", "16:9" },
+ }} },
+ /* Metal Slug X */
+ { R"(\\mslugx\.exe$)", {{
+ { "d3d9.supportD32", "False" },
+ }} },
+ /* Skyrim (NVAPI) */
+ { R"(\\TESV\.exe$)", {{
+ { "d3d9.customVendorId", "1002" },
+ }} },
+ /* RTHDRIBL Demo
+ Uses DONOTWAIT after GetRenderTargetData
+ then goes into an infinite loop if it gets
+ D3DERR_WASSTILLDRAWING.
+ This is a better solution than penalizing
+ other apps that use this properly. */
+ { R"(\\rthdribl\.exe$)", {{
+ { "d3d9.allowDoNotWait", "False" },
+ }} },
+ /* Hyperdimension Neptunia U: Action Unleashed */
+ { R"(\\Neptunia\.exe$)", {{
+ { "d3d9.forceAspectRatio", "16:9" },
+ }} },
+ /* D&D - The Temple Of Elemental Evil */
+ { R"(\\ToEE\.exe$)", {{
+ { "d3d9.allowDiscard", "False" },
+ }} },
+ /* ZUSI 3 - Aerosoft Edition */
+ { R"(\\ZusiSim\.exe$)", {{
+ { "d3d9.noExplicitFrontBuffer", "True" },
+ }} },
+ /* GTA IV (NVAPI) */
+ /* Also thinks we're always on Intel *
+ * and will report/use bad amounts of VRAM. */
+ { R"(\\GTAIV\.exe$)", {{
+ { "d3d9.customVendorId", "1002" },
+ { "dxgi.emulateUMA", "True" },
+ }} },
+ /* Battlefield 2 (bad z-pass) */
+ { R"(\\BF2\.exe$)", {{
+ { "d3d9.longMad", "True" },
+ { "d3d9.invariantPosition", "True" },
+ }} },
+ /* SpellForce 2 Series */
+ { R"(\\SpellForce2.*\.exe$)", {{
+ { "d3d9.forceSamplerTypeSpecConstants", "True" },
+ }} },
+ /* Everquest 2 */
+ { R"(\\EverQuest2.*\.exe$)", {{
+ { "d3d9.alphaTestWiggleRoom", "True" },
+ }} },
+ /* Tomb Raider: Legend */
+ { R"(\\trl\.exe$)", {{
+ { "d3d9.apitraceMode", "True" },
+ }} },
+ /* Everquest */
+ { R"(\\eqgame\.exe$)", {{
+ { "d3d9.apitraceMode", "True" },
+ }} },
+ /* Dark Messiah of Might & Magic */
+ { R"(\\mm\.exe$)", {{
+ { "d3d9.deferSurfaceCreation", "True" },
+ { "d3d9.memoryTrackTest", "True" },
+ }} },
+ /* TrackMania Forever */
+ { R"(\\TmForever\.exe$)", {{
+ { "d3d9.swvpFloatCount", "256" },
+ { "d3d9.swvpIntCount", "16" },
+ { "d3d9.swvpBoolCount", "16" },
+ }} },
+ /* Mafia 2 */
+ { R"(\\mafia2\.exe$)", {{
+ { "d3d9.customVendorId", "10de" },
+ { "d3d9.customDeviceId", "0402" },
+ }} },
+ /* Warhammer: Online */
+ { R"(\\WAR(-64)?\.exe$)", {{
+ { "d3d9.customVendorId", "1002" },
+ }} },
+ /* Dragon Nest */
+ { R"(\\DragonNest_x64\.exe$)", {{
+ { "d3d9.memoryTrackTest ", "True" },
+ }} },
+ /* Dal Segno */
+ { R"(\\DST\.exe$)", {{
+ { "d3d9.deferSurfaceCreation", "True" },
+ }} },
+ /* Kohan II */
+ { R"(\\k2\.exe$)", {{
+ { "d3d9.memoryTrackTest", "True" },
+ }} },
+ /* Ninja Gaiden Sigma 1/2 */
+ { R"(\\NINJA GAIDEN SIGMA(2)?\.exe$)", {{
+ { "d3d9.deferSurfaceCreation", "True" },
+ }} },
+ /* Demon Stone breaks at frame rates > 60fps */
+ { R"(\\Demonstone\.exe$)", {{
+ { "d3d9.maxFrameRate", "60" },
+ }} },
+ /* Far Cry 1 has worse water rendering when it detects AMD GPUs */
+ { R"(\\FarCry\.exe$)", {{
+ { "d3d9.customVendorId", "10de" },
+ }} },
+ /* Earth Defense Force 5 */
+ { R"(\\EDF5\.exe$)", {{
+ { "dxgi.tearFree", "False" },
+ { "dxgi.syncInterval", "1" },
+ }} },
+ /* Sine Mora EX */
+ { R"(\\SineMoraEX\.exe$)", {{
+ { "d3d9.maxFrameRate", "60" },
+ }} },
+ /* Fantasy Grounds */
+ { R"(\\FantasyGrounds\.exe$)", {{
+ { "d3d9.noExplicitFrontBuffer", "True" },
+ }} },
+ }};
+
+
+ static bool isWhitespace(char ch) {
+ return ch == ' ' || ch == '\x9' || ch == '\r';
+ }
+
+
+ static bool isValidKeyChar(char ch) {
+ return (ch >= '0' && ch <= '9')
+ || (ch >= 'A' && ch <= 'Z')
+ || (ch >= 'a' && ch <= 'z')
+ || (ch == '.' || ch == '_');
+ }
+
+
+ static size_t skipWhitespace(const std::string& line, size_t n) {
+ while (n < line.size() && isWhitespace(line[n]))
+ n += 1;
+ return n;
+ }
+
+
+ struct ConfigContext {
+ bool active;
+ };
+
+
+ static void parseUserConfigLine(Config& config, ConfigContext& ctx, const std::string& line) {
+ std::stringstream key;
+ std::stringstream value;
+
+ // Extract the key
+ size_t n = skipWhitespace(line, 0);
+
+ if (n < line.size() && line[n] == '[') {
+ n += 1;
+
+ size_t e = line.size() - 1;
+ while (e > n && line[e] != ']')
+ e -= 1;
+
+ while (n < e)
+ key << line[n++];
+
+ ctx.active = key.str() == env::getExeName();
+ } else {
+ while (n < line.size() && isValidKeyChar(line[n]))
+ key << line[n++];
+
+ // Check whether the next char is a '='
+ n = skipWhitespace(line, n);
+ if (n >= line.size() || line[n] != '=')
+ return;
+
+ // Extract the value
+ bool insideString = false;
+ n = skipWhitespace(line, n + 1);
+
+ while (n < line.size()) {
+ if (!insideString && isWhitespace(line[n]))
+ break;
+
+ if (line[n] == '"') {
+ insideString = !insideString;
+ n++;
+ } else
+ value << line[n++];
+ }
+
+ if (ctx.active)
+ config.setOption(key.str(), value.str());
+ }
+ }
+
+
+ Config::Config() { }
+ Config::~Config() { }
+
+
+ Config::Config(OptionMap&& options)
+ : m_options(std::move(options)) { }
+
+
+ void Config::merge(const Config& other) {
+ for (auto& pair : other.m_options)
+ m_options.insert(pair);
+ }
+
+
+ void Config::setOption(const std::string& key, const std::string& value) {
+ m_options.insert_or_assign(key, value);
+ }
+
+
+ std::string Config::getOptionValue(const char* option) const {
+ auto iter = m_options.find(option);
+
+ return iter != m_options.end()
+ ? iter->second : std::string();
+ }
+
+
+ bool Config::parseOptionValue(
+ const std::string& value,
+ std::string& result) {
+ result = value;
+ return true;
+ }
+
+
+ bool Config::parseOptionValue(
+ const std::string& value,
+ bool& result) {
+ static const std::array<std::pair<const char*, bool>, 2> s_lookup = {{
+ { "true", true },
+ { "false", false },
+ }};
+
+ return parseStringOption(value,
+ s_lookup.begin(), s_lookup.end(), result);
+ }
+
+
+ bool Config::parseOptionValue(
+ const std::string& value,
+ int32_t& result) {
+ if (value.size() == 0)
+ return false;
+
+ // Parse sign, don't allow '+'
+ int32_t sign = 1;
+ size_t start = 0;
+
+ if (value[0] == '-') {
+ sign = -1;
+ start = 1;
+ }
+
+ // Parse absolute number
+ int32_t intval = 0;
+
+ for (size_t i = start; i < value.size(); i++) {
+ if (value[i] < '0' || value[i] > '9')
+ return false;
+
+ intval *= 10;
+ intval += value[i] - '0';
+ }
+
+ // Apply sign and return
+ result = sign * intval;
+ return true;
+ }
+
+
+ bool Config::parseOptionValue(
+ const std::string& value,
+ Tristate& result) {
+ static const std::array<std::pair<const char*, Tristate>, 3> s_lookup = {{
+ { "true", Tristate::True },
+ { "false", Tristate::False },
+ { "auto", Tristate::Auto },
+ }};
+
+ return parseStringOption(value,
+ s_lookup.begin(), s_lookup.end(), result);
+ }
+
+
+ template<typename I, typename V>
+ bool Config::parseStringOption(
+ std::string str,
+ I begin,
+ I end,
+ V& value) {
+ std::transform(str.begin(), str.end(), str.begin(),
+ [] (unsigned char c) { return (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c; });
+
+ for (auto i = begin; i != end; i++) {
+ if (str == i->first) {
+ value = i->second;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ Config Config::getAppConfig(const std::string& appName) {
+ auto appConfig = std::find_if(g_appDefaults.begin(), g_appDefaults.end(),
+ [&appName] (const std::pair<const char*, Config>& pair) {
+ std::regex expr(pair.first, std::regex::extended | std::regex::icase);
+ return std::regex_search(appName, expr);
+ });
+
+ if (appConfig != g_appDefaults.end()) {
+ // Inform the user that we loaded a default config
+ Logger::info(str::format("Found built-in config:"));
+ return appConfig->second;
+ }
+
+ return Config();
+ }
+
+
+ Config Config::getUserConfig() {
+ Config config;
+
+ // Load either $DXVK_CONFIG_FILE or $PWD/dxvk.conf
+ std::string filePath = env::getEnvVar("DXVK_CONFIG_FILE");
+
+ if (filePath == "")
+ filePath = "dxvk.conf";
+
+ // Open the file if it exists
+#ifdef _WIN32
+ std::ifstream stream(str::tows(filePath.c_str()).c_str());
+#else
+ std::ifstream stream(filePath.c_str());
+#endif
+
+ if (!stream)
+ return config;
+
+ // Inform the user that we loaded a file, might
+ // help when debugging configuration issues
+ Logger::info(str::format("Found config file: ", filePath));
+
+ // Initialize parser context
+ ConfigContext ctx;
+ ctx.active = true;
+
+ // Parse the file line by line
+ std::string line;
+
+ while (std::getline(stream, line))
+ parseUserConfigLine(config, ctx, line);
+
+ return config;
+ }
+
+
+ void Config::logOptions() const {
+ if (!m_options.empty()) {
+ Logger::info("Effective configuration:");
+
+ for (auto& pair : m_options)
+ Logger::info(str::format(" ", pair.first, " = ", pair.second));
+ }
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/config/config.h b/src/libs/dxvk-native-1.9.2a/src/util/config/config.h
new file mode 100644
index 00000000..5951a27c
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/config/config.h
@@ -0,0 +1,152 @@
+#pragma once
+
+#include <string>
+#include <unordered_map>
+
+namespace dxvk {
+
+ /**
+ * \brief Tri-state
+ *
+ * Used to conditionally override
+ * booleans if desired.
+ */
+ enum class Tristate : int32_t {
+ Auto = -1,
+ False = 0,
+ True = 1,
+ };
+
+ /**
+ * \brief Config option set
+ *
+ * Stores configuration options
+ * as a set of key-value pairs.
+ */
+ class Config {
+ using OptionMap = std::unordered_map<std::string, std::string>;
+ public:
+
+ Config();
+ Config(OptionMap&& options);
+ ~Config();
+
+ /**
+ * \brief Merges two configuration sets
+ *
+ * Options specified in this config object will
+ * not be overridden if they are specified in
+ * the second config object.
+ * \param [in] other Config set to merge.
+ */
+ void merge(const Config& other);
+
+ /**
+ * \brief Sets an option
+ *
+ * \param [in] key Option name
+ * \param [in] value Option value
+ */
+ void setOption(
+ const std::string& key,
+ const std::string& value);
+
+ /**
+ * \brief Parses an option value
+ *
+ * Retrieves the option value as a string, and then
+ * tries to convert that string to the given type.
+ * If parsing the string fails because it is either
+ * invalid or if the option is not defined, this
+ * method will return a fallback value.
+ *
+ * Currently, this supports the types \c bool,
+ * \c int32_t, and \c std::string.
+ * \tparam T Return value type
+ * \param [in] option Option name
+ * \param [in] fallback Fallback value
+ * \returns Parsed option value
+ * \returns The parsed option value
+ */
+ template<typename T>
+ T getOption(const char* option, T fallback = T()) const {
+ const std::string& value = getOptionValue(option);
+
+ T result = fallback;
+ parseOptionValue(value, result);
+ return result;
+ }
+
+ /**
+ * \brief Logs option values
+ *
+ * Prints the effective configuration
+ * to the log for debugging purposes.
+ */
+ void logOptions() const;
+
+ /**
+ * \brief Retrieves default options for an app
+ *
+ * \param [in] appName Name of the application
+ * \returns Default options for the application
+ */
+ static Config getAppConfig(const std::string& appName);
+
+ /**
+ * \brief Retrieves user configuration
+ *
+ * Reads options from the configuration file,
+ * if it can be found, or an empty option set.
+ * \returns User-defined configuration options
+ */
+ static Config getUserConfig();
+
+ private:
+
+ OptionMap m_options;
+
+ std::string getOptionValue(
+ const char* option) const;
+
+ static bool parseOptionValue(
+ const std::string& value,
+ std::string& result);
+
+ static bool parseOptionValue(
+ const std::string& value,
+ bool& result);
+
+ static bool parseOptionValue(
+ const std::string& value,
+ int32_t& result);
+
+ static bool parseOptionValue(
+ const std::string& value,
+ Tristate& result);
+
+ template<typename I, typename V>
+ static bool parseStringOption(
+ std::string str,
+ I begin,
+ I end,
+ V& value);
+
+ };
+
+
+ /**
+ * \brief Applies tristate option
+ *
+ * Overrides the given value if \c state is
+ * \c True or \c False, and leaves it intact
+ * otherwise.
+ * \param [out] option The value to override
+ * \param [in] state Tristate to apply
+ */
+ inline void applyTristate(bool& option, Tristate state) {
+ option &= state != Tristate::False;
+ option |= state == Tristate::True;
+ }
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/log/log.cpp b/src/libs/dxvk-native-1.9.2a/src/util/log/log.cpp
new file mode 100644
index 00000000..e096ee2f
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/log/log.cpp
@@ -0,0 +1,146 @@
+#ifdef VBOX
+#include <iprt/log.h>
+#endif
+
+#include "log.h"
+
+#include "../util_env.h"
+
+namespace dxvk {
+
+ Logger::Logger(const std::string& file_name)
+ : m_minLevel(getMinLogLevel()) {
+ if (m_minLevel != LogLevel::None) {
+ auto path = getFileName(file_name);
+
+ if (!path.empty()) {
+#ifdef _WIN32
+ m_fileStream = std::ofstream(str::tows(path.c_str()).c_str());
+#else
+ m_fileStream = std::ofstream(path.c_str());
+#endif
+ }
+ }
+ }
+
+
+ Logger::~Logger() { }
+
+
+ void Logger::trace(const std::string& message) {
+#ifndef VBOX
+ s_instance.emitMsg(LogLevel::Trace, message);
+#else
+ LogRel2(("%s", message.c_str()));
+#endif
+ }
+
+
+ void Logger::debug(const std::string& message) {
+#ifndef VBOX
+ s_instance.emitMsg(LogLevel::Debug, message);
+#else
+ LogFlow(("%s", message.c_str()));
+#endif
+ }
+
+
+ void Logger::info(const std::string& message) {
+#ifndef VBOX
+ s_instance.emitMsg(LogLevel::Info, message);
+#else
+ Log(("%s", message.c_str()));
+#endif
+ }
+
+
+ void Logger::warn(const std::string& message) {
+#ifndef VBOX
+ s_instance.emitMsg(LogLevel::Warn, message);
+#else
+ LogRel(("%s", message.c_str()));
+#endif
+ }
+
+
+ void Logger::err(const std::string& message) {
+#ifndef VBOX
+ s_instance.emitMsg(LogLevel::Error, message);
+#else
+ LogRel(("%s", message.c_str()));
+#endif
+ }
+
+
+ void Logger::log(LogLevel level, const std::string& message) {
+#ifndef VBOX
+ s_instance.emitMsg(level, message);
+#else
+ Log(("%s", message.c_str()));
+#endif
+ }
+
+#ifndef VBOX
+ void Logger::emitMsg(LogLevel level, const std::string& message) {
+ if (level >= m_minLevel) {
+ std::lock_guard<dxvk::mutex> lock(m_mutex);
+
+ static std::array<const char*, 5> s_prefixes
+ = {{ "trace: ", "debug: ", "info: ", "warn: ", "err: " }};
+
+ const char* prefix = s_prefixes.at(static_cast<uint32_t>(level));
+
+ std::stringstream stream(message);
+ std::string line;
+
+ while (std::getline(stream, line, '\n')) {
+ std::cerr << prefix << line << std::endl;
+
+ if (m_fileStream)
+ m_fileStream << prefix << line << std::endl;
+ }
+ }
+ }
+#endif
+
+ LogLevel Logger::getMinLogLevel() {
+#ifndef VBOX
+ const std::array<std::pair<const char*, LogLevel>, 6> logLevels = {{
+ { "trace", LogLevel::Trace },
+ { "debug", LogLevel::Debug },
+ { "info", LogLevel::Info },
+ { "warn", LogLevel::Warn },
+ { "error", LogLevel::Error },
+ { "none", LogLevel::None },
+ }};
+
+ const std::string logLevelStr = env::getEnvVar("DXVK_LOG_LEVEL");
+
+ for (const auto& pair : logLevels) {
+ if (logLevelStr == pair.first)
+ return pair.second;
+ }
+#endif
+ return LogLevel::Info;
+ }
+
+
+ std::string Logger::getFileName(const std::string& base) {
+#ifndef VBOX
+ std::string path = env::getEnvVar("DXVK_LOG_PATH");
+
+ if (path == "none")
+ return "";
+
+ if (!path.empty() && *path.rbegin() != '/')
+ path += '/';
+
+ std::string exeName = env::getExeBaseName();
+ path += exeName + "_" + base;
+ return path;
+#else
+ return "";
+#endif
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/log/log.h b/src/libs/dxvk-native-1.9.2a/src/util/log/log.h
new file mode 100644
index 00000000..93c05ffb
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/log/log.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include <array>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "../thread.h"
+
+namespace dxvk {
+
+ enum class LogLevel : uint32_t {
+ Trace = 0,
+ Debug = 1,
+ Info = 2,
+ Warn = 3,
+ Error = 4,
+ None = 5,
+ };
+
+ /**
+ * \brief Logger
+ *
+ * Logger for one DLL. Creates a text file and
+ * writes all log messages to that file.
+ */
+ class Logger {
+
+ public:
+
+ Logger(const std::string& file_name);
+ ~Logger();
+
+ static void trace(const std::string& message);
+ static void debug(const std::string& message);
+ static void info (const std::string& message);
+ static void warn (const std::string& message);
+ static void err (const std::string& message);
+ static void log (LogLevel level, const std::string& message);
+
+ static LogLevel logLevel() {
+#ifndef VBOX
+ return s_instance.m_minLevel;
+#else
+ return LogLevel::Info;
+#endif
+ }
+
+ private:
+
+#ifndef VBOX
+ static Logger s_instance;
+#endif
+
+ const LogLevel m_minLevel;
+
+ dxvk::mutex m_mutex;
+ std::ofstream m_fileStream;
+
+#ifndef VBOX
+ void emitMsg(LogLevel level, const std::string& message);
+#endif
+
+ static LogLevel getMinLogLevel();
+
+ static std::string getFileName(
+ const std::string& base);
+
+ };
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/log/log_debug.cpp b/src/libs/dxvk-native-1.9.2a/src/util/log/log_debug.cpp
new file mode 100644
index 00000000..c67742ab
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/log/log_debug.cpp
@@ -0,0 +1,11 @@
+#include "log_debug.h"
+
+namespace dxvk::debug {
+
+ std::string methodName(const std::string& prettyName) {
+ size_t end = prettyName.find("(");
+ size_t begin = prettyName.substr(0, end).rfind(" ") + 1;
+ return prettyName.substr(begin,end - begin);
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/log/log_debug.h b/src/libs/dxvk-native-1.9.2a/src/util/log/log_debug.h
new file mode 100644
index 00000000..c5084320
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/log/log_debug.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <sstream>
+
+#include "log.h"
+
+#ifdef _MSC_VER
+#define METHOD_NAME __FUNCSIG__
+#else
+#define METHOD_NAME __PRETTY_FUNCTION__
+#endif
+
+#define TRACE_ENABLED
+
+#ifdef TRACE_ENABLED
+#define TRACE(...) \
+ do { dxvk::debug::trace(METHOD_NAME, ##__VA_ARGS__); } while (0)
+#else
+#define TRACE(...) \
+ do { } while (0)
+#endif
+
+namespace dxvk::debug {
+
+ std::string methodName(const std::string& prettyName);
+
+ inline void traceArgs(std::stringstream& stream) { }
+
+ template<typename Arg1>
+ void traceArgs(std::stringstream& stream, const Arg1& arg1) {
+ stream << arg1;
+ }
+
+ template<typename Arg1, typename Arg2, typename... Args>
+ void traceArgs(std::stringstream& stream, const Arg1& arg1, const Arg2& arg2, const Args&... args) {
+ stream << arg1 << ",";
+ traceArgs(stream, arg2, args...);
+ }
+
+ template<typename... Args>
+ void trace(const std::string& funcName, const Args&... args) {
+ std::stringstream stream;
+ stream << methodName(funcName) << "(";
+ traceArgs(stream, args...);
+ stream << ")";
+ Logger::trace(stream.str());
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/meson.build b/src/libs/dxvk-native-1.9.2a/src/util/meson.build
new file mode 100644
index 00000000..dbaba372
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/meson.build
@@ -0,0 +1,49 @@
+util_src = [
+ 'util_env.cpp',
+ 'util_fps_limiter.cpp',
+ 'util_matrix.cpp',
+ 'util_monitor.cpp',
+
+ 'com/com_guid.cpp',
+ 'com/com_private_data.cpp',
+
+ 'config/config.cpp',
+
+ 'log/log.cpp',
+ 'log/log_debug.cpp',
+
+ 'sha1/sha1.c',
+ 'sha1/sha1_util.cpp',
+
+ 'sync/sync_recursive.cpp',
+]
+
+util_src_win32 = [
+ 'util_gdi.cpp',
+
+ 'platform/util_luid_win32.cpp',
+ 'platform/util_env_win32.cpp',
+ 'platform/util_string_win32.cpp'
+]
+
+util_src_linux = [
+ 'platform/util_luid_linux.cpp',
+ 'platform/util_env_linux.cpp',
+ 'platform/util_string_linux.cpp',
+ 'platform/thread_native.cpp',
+]
+
+if dxvk_platform == 'windows'
+ util_src += util_src_win32
+elif dxvk_platform == 'linux'
+ util_src += util_src_linux
+else
+ error('Unknown platform for util')
+endif
+
+util_lib = static_library('util', util_src,
+ include_directories : [ dxvk_include_path ],
+ override_options : ['cpp_std='+dxvk_cpp_std])
+
+util_dep = declare_dependency(
+ link_with : [ util_lib ])
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/platform/thread_native.cpp b/src/libs/dxvk-native-1.9.2a/src/util/platform/thread_native.cpp
new file mode 100644
index 00000000..5337afd4
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/platform/thread_native.cpp
@@ -0,0 +1,21 @@
+#include "../thread.h"
+#include "../util_likely.h"
+
+#include <atomic>
+
+namespace dxvk::this_thread {
+
+ std::atomic<uint32_t> g_threadCtr = { 0u };
+ thread_local uint32_t g_threadId = 0u;
+
+ // This implementation returns thread ids unique to the current instance.
+ // Ie. if you use this across multiple .so's then you might get conflicting ids.
+ // This isn't an issue for us as we only use it in d3d11, but do check if this changes.
+ uint32_t get_id() {
+ if (unlikely(!g_threadId))
+ g_threadId = ++g_threadCtr;
+
+ return g_threadId;
+ }
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/platform/util_env_linux.cpp b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_env_linux.cpp
new file mode 100644
index 00000000..25b03edc
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_env_linux.cpp
@@ -0,0 +1,44 @@
+#include "util_env.h"
+
+ #include <array>
+#ifndef VBOX
+#include <filesystem>
+#endif
+#include <unistd.h>
+#include <limits.h>
+
+#ifdef VBOX
+# include <iprt/process.h>
+# include <iprt/path.h>
+#endif
+
+namespace dxvk::env {
+
+ std::string getExePath() {
+#ifndef VBOX
+ std::array<char, PATH_MAX> exePath = {};
+
+ size_t count = readlink("/proc/self/exe", exePath.data(), exePath.size());
+
+ return std::string(exePath.begin(), exePath.begin() + count);
+#else
+ char szExePath[RTPATH_MAX];
+ RTProcGetExecutablePath(&szExePath[0], sizeof(szExePath));
+ return std::string(&szExePath[0]);
+#endif
+ }
+
+
+ void setThreadName(const std::string& name) {
+ }
+
+
+ bool createDirectory(const std::string& path) {
+#ifndef VBOX
+ return std::filesystem::create_directories(path);
+#else
+ return false;
+#endif
+ }
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/platform/util_env_win32.cpp b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_env_win32.cpp
new file mode 100644
index 00000000..8b86bccf
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_env_win32.cpp
@@ -0,0 +1,38 @@
+#include "util_env.h"
+
+#include "./com/com_include.h"
+
+namespace dxvk::env {
+
+ std::string getExePath() {
+ std::vector<WCHAR> exePath;
+ exePath.resize(MAX_PATH + 1);
+
+ DWORD len = ::GetModuleFileNameW(NULL, exePath.data(), MAX_PATH);
+ exePath.resize(len);
+
+ return str::fromws(exePath.data());
+ }
+
+
+ void setThreadName(const std::string& name) {
+ using SetThreadDescriptionProc = HRESULT (WINAPI *) (HANDLE, PCWSTR);
+
+ static auto proc = reinterpret_cast<SetThreadDescriptionProc>(
+ ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "SetThreadDescription"));
+
+ if (proc != nullptr) {
+ auto wideName = std::vector<WCHAR>(name.length() + 1);
+ str::tows(name.c_str(), wideName.data(), wideName.size());
+ (*proc)(::GetCurrentThread(), wideName.data());
+ }
+ }
+
+
+ bool createDirectory(const std::string& path) {
+ WCHAR widePath[MAX_PATH];
+ str::tows(path.c_str(), widePath);
+ return !!CreateDirectoryW(widePath, nullptr);
+ }
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/platform/util_luid_linux.cpp b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_luid_linux.cpp
new file mode 100644
index 00000000..9c3aa05b
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_luid_linux.cpp
@@ -0,0 +1,13 @@
+#include "util_luid.h"
+
+#include "./log/log.h"
+
+namespace dxvk {
+
+ LUID GetAdapterLUID(UINT Adapter) {
+ Logger::warn("GetAdapterLUID: native stub");
+
+ return LUID();
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/platform/util_luid_win32.cpp b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_luid_win32.cpp
new file mode 100644
index 00000000..96a2a09a
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_luid_win32.cpp
@@ -0,0 +1,34 @@
+#include "util_luid.h"
+#include "util_string.h"
+
+#include "./log/log.h"
+
+#include <mutex>
+#include <vector>
+
+namespace dxvk {
+
+ LUID GetAdapterLUID(UINT Adapter) {
+ static dxvk::mutex s_mutex;
+ static std::vector<LUID> s_luids;
+
+ std::lock_guard<dxvk::mutex> lock(s_mutex);
+ uint32_t newLuidCount = Adapter + 1;
+
+ while (s_luids.size() < newLuidCount) {
+ LUID luid = { 0, 0 };
+
+ if (!::AllocateLocallyUniqueId(&luid))
+ Logger::err("Failed to allocate LUID");
+
+
+ Logger::info(str::format("Adapter LUID ", s_luids.size(), ": ",
+ std::hex, luid.HighPart, ":", luid.LowPart, std::dec));
+
+ s_luids.push_back(luid);
+ }
+
+ return s_luids[Adapter];
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/platform/util_string_linux.cpp b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_string_linux.cpp
new file mode 100644
index 00000000..dbce6458
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_string_linux.cpp
@@ -0,0 +1,19 @@
+#include "util_string.h"
+
+#include <string>
+#include <algorithm>
+
+namespace dxvk::str {
+
+ std::string fromws(const WCHAR *ws) {
+ size_t count = wcslen(ws);
+
+ return std::string(ws, ws + count);
+ }
+
+
+ void tows(const char* mbs, WCHAR* wcs, size_t wcsLen) {
+ std::mbstowcs(wcs, mbs, wcsLen);
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/platform/util_string_win32.cpp b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_string_win32.cpp
new file mode 100644
index 00000000..2151c1e4
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/platform/util_string_win32.cpp
@@ -0,0 +1,43 @@
+#include "util_string.h"
+
+namespace dxvk::str {
+ std::string fromws(const WCHAR *ws) {
+ size_t len = ::WideCharToMultiByte(CP_UTF8,
+ 0, ws, -1, nullptr, 0, nullptr, nullptr);
+
+ if (len <= 1)
+ return "";
+
+ len -= 1;
+
+ std::string result;
+ result.resize(len);
+ ::WideCharToMultiByte(CP_UTF8, 0, ws, -1,
+ &result.at(0), len, nullptr, nullptr);
+ return result;
+ }
+
+
+ void tows(const char* mbs, WCHAR* wcs, size_t wcsLen) {
+ ::MultiByteToWideChar(
+ CP_UTF8, 0, mbs, -1,
+ wcs, wcsLen);
+ }
+
+ std::wstring tows(const char* mbs) {
+ size_t len = ::MultiByteToWideChar(CP_UTF8,
+ 0, mbs, -1, nullptr, 0);
+
+ if (len <= 1)
+ return L"";
+
+ len -= 1;
+
+ std::wstring result;
+ result.resize(len);
+ ::MultiByteToWideChar(CP_UTF8, 0, mbs, -1,
+ &result.at(0), len);
+ return result;
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/rc/util_rc.h b/src/libs/dxvk-native-1.9.2a/src/util/rc/util_rc.h
new file mode 100644
index 00000000..c33d0c61
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/rc/util_rc.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <atomic>
+
+namespace dxvk {
+
+ /**
+ * \brief Reference-counted object
+ */
+ class RcObject {
+
+ public:
+
+ /**
+ * \brief Increments reference count
+ * \returns New reference count
+ */
+ uint32_t incRef() {
+ return ++m_refCount;
+ }
+
+ /**
+ * \brief Decrements reference count
+ * \returns New reference count
+ */
+ uint32_t decRef() {
+ return --m_refCount;
+ }
+
+ private:
+
+ std::atomic<uint32_t> m_refCount = { 0u };
+
+ };
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/rc/util_rc_ptr.h b/src/libs/dxvk-native-1.9.2a/src/util/rc/util_rc_ptr.h
new file mode 100644
index 00000000..23c5c43d
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/rc/util_rc_ptr.h
@@ -0,0 +1,124 @@
+#pragma once
+
+#include <functional>
+#include <ostream>
+
+namespace dxvk {
+
+ /**
+ * \brief Pointer for reference-counted objects
+ *
+ * This only requires the given type to implement \c incRef
+ * and \c decRef methods that adjust the reference count.
+ * \tparam T Object type
+ */
+ template<typename T>
+ class Rc {
+ template<typename Tx>
+ friend class Rc;
+ public:
+
+ Rc() { }
+ Rc(std::nullptr_t) { }
+
+ Rc(T* object)
+ : m_object(object) {
+ this->incRef();
+ }
+
+ Rc(const Rc& other)
+ : m_object(other.m_object) {
+ this->incRef();
+ }
+
+ template<typename Tx>
+ Rc(const Rc<Tx>& other)
+ : m_object(other.m_object) {
+ this->incRef();
+ }
+
+ Rc(Rc&& other)
+ : m_object(other.m_object) {
+ other.m_object = nullptr;
+ }
+
+ template<typename Tx>
+ Rc(Rc<Tx>&& other)
+ : m_object(other.m_object) {
+ other.m_object = nullptr;
+ }
+
+ Rc& operator = (std::nullptr_t) {
+ this->decRef();
+ m_object = nullptr;
+ return *this;
+ }
+
+ Rc& operator = (const Rc& other) {
+ other.incRef();
+ this->decRef();
+ m_object = other.m_object;
+ return *this;
+ }
+
+ template<typename Tx>
+ Rc& operator = (const Rc<Tx>& other) {
+ other.incRef();
+ this->decRef();
+ m_object = other.m_object;
+ return *this;
+ }
+
+ Rc& operator = (Rc&& other) {
+ this->decRef();
+ this->m_object = other.m_object;
+ other.m_object = nullptr;
+ return *this;
+ }
+
+ template<typename Tx>
+ Rc& operator = (Rc<Tx>&& other) {
+ this->decRef();
+ this->m_object = other.m_object;
+ other.m_object = nullptr;
+ return *this;
+ }
+
+ ~Rc() {
+ this->decRef();
+ }
+
+ T& operator * () const { return *m_object; }
+ T* operator -> () const { return m_object; }
+ T* ptr() const { return m_object; }
+
+ bool operator == (const Rc& other) const { return m_object == other.m_object; }
+ bool operator != (const Rc& other) const { return m_object != other.m_object; }
+
+ bool operator == (std::nullptr_t) const { return m_object == nullptr; }
+ bool operator != (std::nullptr_t) const { return m_object != nullptr; }
+
+ private:
+
+ T* m_object = nullptr;
+
+ void incRef() const {
+ if (m_object != nullptr)
+ m_object->incRef();
+ }
+
+ void decRef() const {
+ if (m_object != nullptr) {
+ if (m_object->decRef() == 0)
+ delete m_object;
+ }
+ }
+
+ };
+
+}
+
+template<typename T>
+std::ostream& operator << (std::ostream& os, const dxvk::Rc<T>& rc) {
+ return os << rc.ptr();
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1.c b/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1.c
new file mode 100644
index 00000000..39e60675
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1.c
@@ -0,0 +1,170 @@
+/* $OpenBSD: sha1.c,v 1.26 2015/09/11 09:18:27 guenther Exp $ */
+
+/*
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ *
+ * Test Vectors (from FIPS PUB 180-1)
+ * "abc"
+ * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ * A million repetitions of "a"
+ * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "sha1.h"
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/*
+ * blk0() and blk() perform the initial expand.
+ * I got the idea of expanding during the round function from SSLeay
+ */
+# define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/*
+ * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
+ */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+typedef union {
+ uint8_t c[64];
+ uint32_t l[16];
+} CHAR64LONG16;
+
+/*
+ * Hash a single 512-bit block. This is the core of the algorithm.
+ */
+void
+SHA1Transform(uint32_t state[5], const uint8_t buffer[SHA1_BLOCK_LENGTH])
+{
+ uint32_t a, b, c, d, e;
+ uint8_t workspace[SHA1_BLOCK_LENGTH];
+ CHAR64LONG16 *block = (CHAR64LONG16 *)workspace;
+
+ (void)memcpy(block, buffer, SHA1_BLOCK_LENGTH);
+
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+}
+
+
+/*
+ * SHA1Init - Initialize new context
+ */
+void
+SHA1Init(SHA1_CTX *context)
+{
+
+ /* SHA1 initialization constants */
+ context->count = 0;
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+}
+
+
+/*
+ * Run your data through this.
+ */
+void
+SHA1Update(SHA1_CTX *context, const uint8_t *data, size_t len)
+{
+ size_t i, j;
+
+ j = (size_t)((context->count >> 3) & 63);
+ context->count += (len << 3);
+ if ((j + len) > 63) {
+ (void)memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64)
+ SHA1Transform(context->state, (uint8_t *)&data[i]);
+ j = 0;
+ } else {
+ i = 0;
+ }
+ (void)memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/*
+ * Add padding and return the message digest.
+ */
+void
+SHA1Pad(SHA1_CTX *context)
+{
+ uint8_t finalcount[8];
+ uint32_t i;
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (uint8_t)((context->count >>
+ ((7 - (i & 7)) * 8)) & 255); /* Endian independent */
+ }
+ SHA1Update(context, (uint8_t *)"\200", 1);
+ while ((context->count & 504) != 448)
+ SHA1Update(context, (uint8_t *)"\0", 1);
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+}
+
+void
+SHA1Final(uint8_t digest[SHA1_DIGEST_LENGTH], SHA1_CTX *context)
+{
+ uint32_t i;
+
+ SHA1Pad(context);
+ for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
+ digest[i] = (uint8_t)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ memset(context, 0, sizeof(*context));
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1.h b/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1.h
new file mode 100644
index 00000000..029a0ae8
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1.h
@@ -0,0 +1,53 @@
+/* $OpenBSD: sha1.h,v 1.24 2012/12/05 23:19:57 deraadt Exp $ */
+
+/*
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ */
+
+#ifndef _SHA1_H
+#define _SHA1_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define SHA1_BLOCK_LENGTH 64
+#define SHA1_DIGEST_LENGTH 20
+#define SHA1_DIGEST_STRING_LENGTH (SHA1_DIGEST_LENGTH * 2 + 1)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _SHA1_CTX {
+ uint32_t state[5];
+ uint64_t count;
+ uint8_t buffer[SHA1_BLOCK_LENGTH];
+} SHA1_CTX;
+
+void SHA1Init(SHA1_CTX *);
+void SHA1Pad(SHA1_CTX *);
+void SHA1Transform(uint32_t [5], const uint8_t [SHA1_BLOCK_LENGTH]);
+void SHA1Update(SHA1_CTX *, const uint8_t *, size_t);
+void SHA1Final(uint8_t [SHA1_DIGEST_LENGTH], SHA1_CTX *);
+
+#define HTONDIGEST(x) do { \
+ x[0] = htonl(x[0]); \
+ x[1] = htonl(x[1]); \
+ x[2] = htonl(x[2]); \
+ x[3] = htonl(x[3]); \
+ x[4] = htonl(x[4]); } while (0)
+
+#define NTOHDIGEST(x) do { \
+ x[0] = ntohl(x[0]); \
+ x[1] = ntohl(x[1]); \
+ x[2] = ntohl(x[2]); \
+ x[3] = ntohl(x[3]); \
+ x[4] = ntohl(x[4]); } while (0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SHA1_H */
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1_util.cpp b/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1_util.cpp
new file mode 100644
index 00000000..eefc9275
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1_util.cpp
@@ -0,0 +1,48 @@
+#include "sha1.h"
+#include "sha1_util.h"
+
+namespace dxvk {
+
+ std::string Sha1Hash::toString() const {
+ static const char nibbles[]
+ = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ std::string result;
+ result.resize(2 * m_digest.size());
+
+ for (uint32_t i = 0; i < m_digest.size(); i++) {
+ result.at(2 * i + 0) = nibbles[(m_digest[i] >> 4) & 0xF];
+ result.at(2 * i + 1) = nibbles[(m_digest[i] >> 0) & 0xF];
+ }
+
+ return result;
+ }
+
+
+ Sha1Hash Sha1Hash::compute(
+ const void* data,
+ size_t size) {
+ Sha1Data chunk = { data, size };
+ return compute(1, &chunk);
+ }
+
+
+ Sha1Hash Sha1Hash::compute(
+ size_t numChunks,
+ const Sha1Data* chunks) {
+ Sha1Digest digest;
+
+ SHA1_CTX ctx;
+ SHA1Init(&ctx);
+
+ for (size_t i = 0; i < numChunks; i++) {
+ auto ptr = reinterpret_cast<const uint8_t*>(chunks[i].data);
+ SHA1Update(&ctx, ptr, chunks[i].size);
+ }
+
+ SHA1Final(digest.data(), &ctx);
+ return Sha1Hash(digest);
+ }
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1_util.h b/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1_util.h
new file mode 100644
index 00000000..9d83ab8c
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/sha1/sha1_util.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <array>
+#include <cstring>
+#include <string>
+
+namespace dxvk {
+
+ using Sha1Digest = std::array<uint8_t, 20>;
+
+ struct Sha1Data {
+ const void* data;
+ size_t size;
+ };
+
+ class Sha1Hash {
+
+ public:
+
+ Sha1Hash() { }
+ Sha1Hash(const Sha1Digest& digest)
+ : m_digest(digest) { }
+
+ std::string toString() const;
+
+ uint32_t dword(uint32_t id) const {
+ return uint32_t(m_digest[4 * id + 0]) << 0
+ | uint32_t(m_digest[4 * id + 1]) << 8
+ | uint32_t(m_digest[4 * id + 2]) << 16
+ | uint32_t(m_digest[4 * id + 3]) << 24;
+ }
+
+ bool operator == (const Sha1Hash& other) const {
+ return !std::memcmp(
+ this->m_digest.data(),
+ other.m_digest.data(),
+ other.m_digest.size());
+ }
+
+ bool operator != (const Sha1Hash& other) const {
+ return !this->operator == (other);
+ }
+
+ static Sha1Hash compute(
+ const void* data,
+ size_t size);
+
+ static Sha1Hash compute(
+ size_t numChunks,
+ const Sha1Data* chunks);
+
+ template<typename T>
+ static Sha1Hash compute(const T& data) {
+ return compute(&data, sizeof(T));
+ }
+
+ private:
+
+ Sha1Digest m_digest;
+
+ };
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_recursive.cpp b/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_recursive.cpp
new file mode 100644
index 00000000..180d3f1f
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_recursive.cpp
@@ -0,0 +1,37 @@
+#include "sync_recursive.h"
+#include "sync_spinlock.h"
+#include "../thread.h"
+
+namespace dxvk::sync {
+
+ void RecursiveSpinlock::lock() {
+ spin(2000, [this] { return try_lock(); });
+ }
+
+
+ void RecursiveSpinlock::unlock() {
+ if (likely(m_counter == 0))
+ m_owner.store(0, std::memory_order_release);
+ else
+ m_counter -= 1;
+ }
+
+
+ bool RecursiveSpinlock::try_lock() {
+ uint32_t threadId = dxvk::this_thread::get_id();
+ uint32_t expected = 0;
+
+ bool status = m_owner.compare_exchange_weak(
+ expected, threadId, std::memory_order_acquire);
+
+ if (status)
+ return true;
+
+ if (expected != threadId)
+ return false;
+
+ m_counter += 1;
+ return true;
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_recursive.h b/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_recursive.h
new file mode 100644
index 00000000..1bb64db1
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_recursive.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <atomic>
+
+#include "../com/com_include.h"
+
+namespace dxvk::sync {
+
+ /**
+ * \brief Recursive spinlock
+ *
+ * Implements a spinlock that can be acquired
+ * by the same thread multiple times.
+ */
+ class RecursiveSpinlock {
+
+ public:
+
+ void lock();
+
+ void unlock();
+
+ bool try_lock();
+
+ private:
+
+ std::atomic<uint32_t> m_owner = { 0u };
+ uint32_t m_counter = { 0u };
+
+ };
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_signal.h b/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_signal.h
new file mode 100644
index 00000000..cdbcae82
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_signal.h
@@ -0,0 +1,160 @@
+#pragma once
+
+#include <atomic>
+#include <functional>
+#include <list>
+
+#include "../rc/util_rc.h"
+
+#include "../thread.h"
+
+namespace dxvk::sync {
+
+ /**
+ * \brief Signal
+ *
+ * Interface for a CPU-side fence. Can be signaled
+ * to a given value, and any thread waiting for a
+ * lower value will be woken up.
+ */
+ class Signal : public RcObject {
+
+ public:
+
+ virtual ~Signal() { }
+
+ /**
+ * \brief Last signaled value
+ * \returns Last signaled value
+ */
+ virtual uint64_t value() const = 0;
+
+ /**
+ * \brief Notifies signal
+ *
+ * Wakes up all threads currently waiting for
+ * a value lower than \c value. Note that
+ * \c value must monotonically increase.
+ * \param [in] value Value to signal to
+ */
+ virtual void signal(uint64_t value) = 0;
+
+ /**
+ * \brief Waits for signal
+ *
+ * Blocks the calling thread until another
+ * thread signals it with a value equal to
+ * or greater than \c value.
+ * \param [in] value The value to wait for
+ */
+ virtual void wait(uint64_t value) = 0;
+
+ };
+
+
+ /**
+ * \brief Fence
+ *
+ * Simple CPU-side fence.
+ */
+ class Fence final : public Signal {
+
+ public:
+
+ Fence()
+ : m_value(0ull) { }
+
+ explicit Fence(uint64_t value)
+ : m_value(value) { }
+
+ uint64_t value() const {
+ return m_value.load(std::memory_order_acquire);
+ }
+
+ void signal(uint64_t value) {
+ std::unique_lock<dxvk::mutex> lock(m_mutex);
+ m_value.store(value, std::memory_order_release);
+ m_cond.notify_all();
+ }
+
+ void wait(uint64_t value) {
+ std::unique_lock<dxvk::mutex> lock(m_mutex);
+ m_cond.wait(lock, [this, value] {
+ return value <= m_value.load(std::memory_order_acquire);
+ });
+ }
+
+ private:
+
+ std::atomic<uint64_t> m_value;
+ dxvk::mutex m_mutex;
+ dxvk::condition_variable m_cond;
+
+ };
+
+
+ /**
+ * \brief Callback signal
+ *
+ * CPU-side fence with the ability to call a
+ * function when signaled to a given value.
+ */
+ class CallbackFence final : public Signal {
+
+ public:
+
+ CallbackFence()
+ : m_value(0ull) { }
+
+ explicit CallbackFence(uint64_t value)
+ : m_value(value) { }
+
+ uint64_t value() const {
+ return m_value.load(std::memory_order_acquire);
+ }
+
+ void signal(uint64_t value) {
+ std::unique_lock<dxvk::mutex> lock(m_mutex);
+ m_value.store(value, std::memory_order_release);
+ m_cond.notify_all();
+
+ for (auto i = m_callbacks.begin(); i != m_callbacks.end(); ) {
+ if (value >= i->first) {
+ i->second();
+ i = m_callbacks.erase(i);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ void wait(uint64_t value) {
+ std::unique_lock<dxvk::mutex> lock(m_mutex);
+ m_cond.wait(lock, [this, value] {
+ return value <= m_value.load(std::memory_order_acquire);
+ });
+ }
+
+ template<typename Fn>
+ void setCallback(uint64_t value, Fn&& proc) {
+ std::unique_lock<dxvk::mutex> lock(m_mutex);
+
+ if (value > this->value())
+ m_callbacks.emplace_back(std::piecewise_construct,
+ std::make_tuple(value),
+ std::make_tuple(proc));
+ else
+ proc();
+ }
+
+ private:
+
+ std::atomic<uint64_t> m_value;
+ dxvk::mutex m_mutex;
+ dxvk::condition_variable m_cond;
+
+ std::list<std::pair<uint64_t, std::function<void ()>>> m_callbacks;
+
+ };
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_spinlock.h b/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_spinlock.h
new file mode 100644
index 00000000..27157929
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_spinlock.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <atomic>
+
+#include "../thread.h"
+
+#include "../util_bit.h"
+#include "../util_likely.h"
+
+namespace dxvk::sync {
+
+ /**
+ * \brief Generic spin function
+ *
+ * Blocks calling thread until a condition becomes
+ * \c true, calling \c yield every few iterations.
+ * \param [in] spinCount Number of probes between each yield
+ * \param [in] fn Condition to test
+ */
+ template<typename Fn>
+ void spin(uint32_t spinCount, const Fn& fn) {
+ while (unlikely(!fn())) {
+ for (uint32_t i = 1; i < spinCount; i++) {
+ _mm_pause();
+ if (fn())
+ return;
+ }
+
+ dxvk::this_thread::yield();
+ }
+ }
+
+ /**
+ * \brief Spin lock
+ *
+ * A low-overhead spin lock which can be used to
+ * protect data structures for a short duration
+ * in case the structure is not likely contested.
+ */
+ class Spinlock {
+
+ public:
+
+ Spinlock() { }
+ ~Spinlock() { }
+
+ Spinlock (const Spinlock&) = delete;
+ Spinlock& operator = (const Spinlock&) = delete;
+
+ void lock() {
+ spin(200, [this] { return try_lock(); });
+ }
+
+ void unlock() {
+ m_lock.store(0, std::memory_order_release);
+ }
+
+ bool try_lock() {
+ return likely(!m_lock.load())
+ && likely(!m_lock.exchange(1, std::memory_order_acquire));
+ }
+
+ private:
+
+ std::atomic<uint32_t> m_lock = { 0 };
+
+ };
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_ticketlock.h b/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_ticketlock.h
new file mode 100644
index 00000000..1c47f839
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/sync/sync_ticketlock.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <atomic>
+
+#include "../thread.h"
+
+namespace dxvk::sync {
+
+ /**
+ * \brief Ticket spinlock
+ *
+ * A fair spinlock implementation that should
+ * be preferred over \ref Spinlock when one of
+ * the threads accessing the lock is likely to
+ * starve another.
+ */
+ class TicketLock {
+
+ public:
+
+ void lock() {
+ uint32_t ticket = m_counter.fetch_add(1);
+
+ while (m_serving.load(std::memory_order_acquire) != ticket)
+ continue;
+ }
+
+ void unlock() {
+ uint32_t serveNext = m_serving.load() + 1;
+ m_serving.store(serveNext, std::memory_order_release);
+ }
+
+ private:
+
+ std::atomic<uint32_t> m_counter = { 0 };
+ std::atomic<uint32_t> m_serving = { 0 };
+
+ };
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/thread.h b/src/libs/dxvk-native-1.9.2a/src/util/thread.h
new file mode 100644
index 00000000..0545faeb
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/thread.h
@@ -0,0 +1,351 @@
+#pragma once
+
+#include <chrono>
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#ifndef _WIN32
+#include <thread>
+#endif
+
+#include "util_error.h"
+
+#include "./com/com_include.h"
+
+#include "./rc/util_rc.h"
+#include "./rc/util_rc_ptr.h"
+
+namespace dxvk {
+
+#ifdef _WIN32
+ /**
+ * \brief Thread priority
+ */
+ enum class ThreadPriority : int32_t {
+ Lowest = THREAD_PRIORITY_LOWEST,
+ Low = THREAD_PRIORITY_BELOW_NORMAL,
+ Normal = THREAD_PRIORITY_NORMAL,
+ High = THREAD_PRIORITY_ABOVE_NORMAL,
+ Highest = THREAD_PRIORITY_HIGHEST,
+ };
+
+ /**
+ * \brief Thread helper class
+ *
+ * This is needed mostly for winelib builds. Wine needs to setup each thread that
+ * calls Windows APIs. It means that in winelib builds, we can't let standard C++
+ * library create threads and need to use Wine for that instead. We use a thin wrapper
+ * around Windows thread functions so that the rest of code just has to use
+ * dxvk::thread class instead of std::thread.
+ */
+ class ThreadFn : public RcObject {
+ using Proc = std::function<void()>;
+ public:
+
+ ThreadFn(Proc&& proc)
+ : m_proc(std::move(proc)) {
+ // Reference for the thread function
+ this->incRef();
+
+ m_handle = ::CreateThread(nullptr, 0x100000,
+ ThreadFn::threadProc, this, STACK_SIZE_PARAM_IS_A_RESERVATION,
+ nullptr);
+
+ if (m_handle == nullptr)
+ throw DxvkError("Failed to create thread");
+ }
+
+ ~ThreadFn() {
+ if (this->joinable())
+ std::terminate();
+ }
+
+ void detach() {
+ ::CloseHandle(m_handle);
+ m_handle = nullptr;
+ }
+
+ void join() {
+ if(::WaitForSingleObjectEx(m_handle, INFINITE, FALSE) == WAIT_FAILED)
+ throw DxvkError("Failed to join thread");
+ this->detach();
+ }
+
+ bool joinable() const {
+ return m_handle != nullptr;
+ }
+
+ void set_priority(ThreadPriority priority) {
+ ::SetThreadPriority(m_handle, int32_t(priority));
+ }
+
+ private:
+
+ Proc m_proc;
+ HANDLE m_handle;
+
+ static DWORD WINAPI threadProc(void *arg) {
+ auto thread = reinterpret_cast<ThreadFn*>(arg);
+ thread->m_proc();
+ thread->decRef();
+ return 0;
+ }
+
+ };
+
+
+ /**
+ * \brief RAII thread wrapper
+ *
+ * Wrapper for \c ThreadFn that can be used
+ * as a drop-in replacement for \c std::thread.
+ */
+ class thread {
+
+ public:
+
+ thread() { }
+
+ explicit thread(std::function<void()>&& func)
+ : m_thread(new ThreadFn(std::move(func))) { }
+
+ thread(thread&& other)
+ : m_thread(std::move(other.m_thread)) { }
+
+ thread& operator = (thread&& other) {
+ m_thread = std::move(other.m_thread);
+ return *this;
+ }
+
+ void detach() {
+ m_thread->detach();
+ }
+
+ void join() {
+ m_thread->join();
+ }
+
+ bool joinable() const {
+ return m_thread != nullptr
+ && m_thread->joinable();
+ }
+
+ void set_priority(ThreadPriority priority) {
+ m_thread->set_priority(priority);
+ }
+
+ static uint32_t hardware_concurrency() {
+ SYSTEM_INFO info = { };
+ ::GetSystemInfo(&info);
+ return info.dwNumberOfProcessors;
+ }
+
+ private:
+
+ Rc<ThreadFn> m_thread;
+
+ };
+
+
+ namespace this_thread {
+ inline void yield() {
+ SwitchToThread();
+ }
+
+ inline uint32_t get_id() {
+ return GetCurrentThreadId();
+ }
+ }
+
+
+ /**
+ * \brief SRW-based mutex implementation
+ *
+ * Drop-in replacement for \c std::mutex that uses Win32
+ * SRW locks, which are implemented with \c futex in wine.
+ */
+ class mutex {
+
+ public:
+
+ using native_handle_type = PSRWLOCK;
+
+ mutex() { }
+
+ mutex(const mutex&) = delete;
+ mutex& operator = (const mutex&) = delete;
+
+ void lock() {
+ AcquireSRWLockExclusive(&m_lock);
+ }
+
+ void unlock() {
+ ReleaseSRWLockExclusive(&m_lock);
+ }
+
+ bool try_lock() {
+ return TryAcquireSRWLockExclusive(&m_lock);
+ }
+
+ native_handle_type native_handle() {
+ return &m_lock;
+ }
+
+ private:
+
+ SRWLOCK m_lock = SRWLOCK_INIT;
+
+ };
+
+
+ /**
+ * \brief Recursive mutex implementation
+ *
+ * Drop-in replacement for \c std::recursive_mutex that
+ * uses Win32 critical sections.
+ */
+ class recursive_mutex {
+
+ public:
+
+ using native_handle_type = PCRITICAL_SECTION;
+
+ recursive_mutex() {
+ InitializeCriticalSection(&m_lock);
+ }
+
+ ~recursive_mutex() {
+ DeleteCriticalSection(&m_lock);
+ }
+
+ recursive_mutex(const recursive_mutex&) = delete;
+ recursive_mutex& operator = (const recursive_mutex&) = delete;
+
+ void lock() {
+ EnterCriticalSection(&m_lock);
+ }
+
+ void unlock() {
+ LeaveCriticalSection(&m_lock);
+ }
+
+ bool try_lock() {
+ return TryEnterCriticalSection(&m_lock);
+ }
+
+ native_handle_type native_handle() {
+ return &m_lock;
+ }
+
+ private:
+
+ CRITICAL_SECTION m_lock;
+
+ };
+
+
+ /**
+ * \brief SRW-based condition variable implementation
+ *
+ * Drop-in replacement for \c std::condition_variable that
+ * uses Win32 condition variables on SRW locks.
+ */
+ class condition_variable {
+
+ public:
+
+ using native_handle_type = PCONDITION_VARIABLE;
+
+ condition_variable() {
+ InitializeConditionVariable(&m_cond);
+ }
+
+ condition_variable(condition_variable&) = delete;
+
+ condition_variable& operator = (condition_variable&) = delete;
+
+ void notify_one() {
+ WakeConditionVariable(&m_cond);
+ }
+
+ void notify_all() {
+ WakeAllConditionVariable(&m_cond);
+ }
+
+ void wait(std::unique_lock<dxvk::mutex>& lock) {
+ auto srw = lock.mutex()->native_handle();
+ SleepConditionVariableSRW(&m_cond, srw, INFINITE, 0);
+ }
+
+ template<typename Predicate>
+ void wait(std::unique_lock<dxvk::mutex>& lock, Predicate pred) {
+ while (!pred())
+ wait(lock);
+ }
+
+ template<typename Clock, typename Duration>
+ std::cv_status wait_until(std::unique_lock<dxvk::mutex>& lock, const std::chrono::time_point<Clock, Duration>& time) {
+ auto now = Clock::now();
+
+ return (now < time)
+ ? wait_for(lock, now - time)
+ : std::cv_status::timeout;
+ }
+
+ template<typename Clock, typename Duration, typename Predicate>
+ bool wait_until(std::unique_lock<dxvk::mutex>& lock, const std::chrono::time_point<Clock, Duration>& time, Predicate pred) {
+ if (pred())
+ return true;
+
+ auto now = Clock::now();
+ return now < time && wait_for(lock, now - time, pred);
+ }
+
+ template<typename Rep, typename Period>
+ std::cv_status wait_for(std::unique_lock<dxvk::mutex>& lock, const std::chrono::duration<Rep, Period>& timeout) {
+ auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(timeout);
+ auto srw = lock.mutex()->native_handle();
+
+ return SleepConditionVariableSRW(&m_cond, srw, ms.count(), 0)
+ ? std::cv_status::no_timeout
+ : std::cv_status::timeout;
+ }
+
+ template<typename Rep, typename Period, typename Predicate>
+ bool wait_for(std::unique_lock<dxvk::mutex>& lock, const std::chrono::duration<Rep, Period>& timeout, Predicate pred) {
+ bool result = pred();
+
+ if (!result && wait_for(lock, timeout) == std::cv_status::no_timeout)
+ result = pred();
+
+ return result;
+ }
+
+ native_handle_type native_handle() {
+ return &m_cond;
+ }
+
+ private:
+
+ CONDITION_VARIABLE m_cond;
+
+ };
+
+#else
+
+ using mutex = std::mutex;
+ using thread = std::thread;
+ using recursive_mutex = std::recursive_mutex;
+ using condition_variable = std::condition_variable;
+
+ namespace this_thread {
+ inline void yield() {
+ std::this_thread::yield();
+ }
+
+ uint32_t get_id();
+ }
+
+#endif
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_bit.h b/src/libs/dxvk-native-1.9.2a/src/util/util_bit.h
new file mode 100644
index 00000000..3c65c70d
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_bit.h
@@ -0,0 +1,349 @@
+#pragma once
+
+#ifndef _MSC_VER
+#if defined(__WINE__) && defined(__clang__)
+#pragma push_macro("_WIN32")
+#undef _WIN32
+#endif
+#include <x86intrin.h>
+#if defined(__WINE__) && defined(__clang__)
+#pragma pop_macro("_WIN32")
+#endif
+#else
+#include <intrin.h>
+#endif
+
+#include "util_likely.h"
+#include "util_math.h"
+
+#include <cstring>
+#include <iterator>
+#include <type_traits>
+
+namespace dxvk::bit {
+
+ template<typename T, typename J>
+ T cast(const J& src) {
+ static_assert(sizeof(T) == sizeof(J));
+ static_assert(std::is_trivially_copyable<J>::value && std::is_trivial<T>::value);
+
+ T dst;
+ std::memcpy(&dst, &src, sizeof(T));
+ return dst;
+ }
+
+ template<typename T>
+ T extract(T value, uint32_t fst, uint32_t lst) {
+ return (value >> fst) & ~(~T(0) << (lst - fst + 1));
+ }
+
+ inline uint32_t popcntStep(uint32_t n, uint32_t mask, uint32_t shift) {
+ return (n & mask) + ((n & ~mask) >> shift);
+ }
+
+ inline uint32_t popcnt(uint32_t n) {
+ n = popcntStep(n, 0x55555555, 1);
+ n = popcntStep(n, 0x33333333, 2);
+ n = popcntStep(n, 0x0F0F0F0F, 4);
+ n = popcntStep(n, 0x00FF00FF, 8);
+ n = popcntStep(n, 0x0000FFFF, 16);
+ return n;
+ }
+
+ inline uint32_t tzcnt(uint32_t n) {
+ #if defined(_MSC_VER) && !defined(__clang__)
+ return _tzcnt_u32(n);
+ #elif defined(__BMI__)
+ return __tzcnt_u32(n);
+ #elif defined(__GNUC__) || defined(__clang__)
+ uint32_t res;
+ uint32_t tmp;
+ asm (
+ "mov $32, %1;"
+ "bsf %2, %0;"
+ "cmovz %1, %0;"
+ : "=&r" (res), "=&r" (tmp)
+ : "r" (n));
+ return res;
+ #else
+ uint32_t r = 31;
+ n &= -n;
+ r -= (n & 0x0000FFFF) ? 16 : 0;
+ r -= (n & 0x00FF00FF) ? 8 : 0;
+ r -= (n & 0x0F0F0F0F) ? 4 : 0;
+ r -= (n & 0x33333333) ? 2 : 0;
+ r -= (n & 0x55555555) ? 1 : 0;
+ return n != 0 ? r : 32;
+ #endif
+ }
+
+ inline uint32_t bsf(uint32_t n) {
+ #if defined(_MSC_VER) && !defined(__clang__)
+ unsigned long index;
+ _BitScanForward(&index, n);
+ return uint32_t(index);
+ #elif defined(__GNUC__) || defined(__clang__)
+ return __builtin_ctz(n);
+ #else
+ uint32_t r = 31;
+ n &= -n;
+ r -= (n & 0x0000FFFF) ? 16 : 0;
+ r -= (n & 0x00FF00FF) ? 8 : 0;
+ r -= (n & 0x0F0F0F0F) ? 4 : 0;
+ r -= (n & 0x33333333) ? 2 : 0;
+ r -= (n & 0x55555555) ? 1 : 0;
+ return r;
+ #endif
+ }
+
+ inline uint32_t lzcnt(uint32_t n) {
+ #if (defined(_MSC_VER) && !defined(__clang__)) || defined(__LZCNT__)
+ return _lzcnt_u32(n);
+ #elif defined(__GNUC__) || defined(__clang__)
+ return n != 0 ? __builtin_clz(n) : 32;
+ #else
+ uint32_t r = 0;
+
+ if (n == 0) return 32;
+
+ if (n <= 0x0000FFFF) { r += 16; n <<= 16; }
+ if (n <= 0x00FFFFFF) { r += 8; n <<= 8; }
+ if (n <= 0x0FFFFFFF) { r += 4; n <<= 4; }
+ if (n <= 0x3FFFFFFF) { r += 2; n <<= 2; }
+ if (n <= 0x7FFFFFFF) { r += 1; n <<= 1; }
+
+ return r;
+ #endif
+ }
+
+ template<typename T>
+ uint32_t pack(T& dst, uint32_t& shift, T src, uint32_t count) {
+ constexpr uint32_t Bits = 8 * sizeof(T);
+ if (likely(shift < Bits))
+ dst |= src << shift;
+ shift += count;
+ return shift > Bits ? shift - Bits : 0;
+ }
+
+ template<typename T>
+ uint32_t unpack(T& dst, T src, uint32_t& shift, uint32_t count) {
+ constexpr uint32_t Bits = 8 * sizeof(T);
+ if (likely(shift < Bits))
+ dst = (src >> shift) & ((T(1) << count) - 1);
+ shift += count;
+ return shift > Bits ? shift - Bits : 0;
+ }
+
+ /**
+ * \brief Compares two aligned structs bit by bit
+ *
+ * \param [in] a First struct
+ * \param [in] b Second struct
+ * \returns \c true if the structs are equal
+ */
+ template<typename T>
+ bool bcmpeq(const T* a, const T* b) {
+ static_assert(alignof(T) >= 16);
+ #if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
+ auto ai = reinterpret_cast<const __m128i*>(a);
+ auto bi = reinterpret_cast<const __m128i*>(b);
+
+ size_t i = 0;
+
+ #if defined(__clang__)
+ #pragma nounroll
+ #elif defined(__GNUC__)
+ #pragma GCC unroll 0
+ #endif
+
+ for ( ; i < 2 * (sizeof(T) / 32); i += 2) {
+ __m128i eq0 = _mm_cmpeq_epi8(
+ _mm_load_si128(ai + i),
+ _mm_load_si128(bi + i));
+ __m128i eq1 = _mm_cmpeq_epi8(
+ _mm_load_si128(ai + i + 1),
+ _mm_load_si128(bi + i + 1));
+ __m128i eq = _mm_and_si128(eq0, eq1);
+
+ int mask = _mm_movemask_epi8(eq);
+ if (mask != 0xFFFF)
+ return false;
+ }
+
+ for ( ; i < sizeof(T) / 16; i++) {
+ __m128i eq = _mm_cmpeq_epi8(
+ _mm_load_si128(ai + i),
+ _mm_load_si128(bi + i));
+
+ int mask = _mm_movemask_epi8(eq);
+ if (mask != 0xFFFF)
+ return false;
+ }
+
+ return true;
+ #else
+ return !std::memcmp(a, b, sizeof(T));
+ #endif
+ }
+
+ template <size_t Bits>
+ class bitset {
+ static constexpr size_t Dwords = align(Bits, 32) / 32;
+ public:
+
+ constexpr bitset()
+ : m_dwords() {
+
+ }
+
+ constexpr bool get(uint32_t idx) const {
+ uint32_t dword = 0;
+ uint32_t bit = idx;
+
+ // Compiler doesn't remove this otherwise.
+ if constexpr (Dwords > 1) {
+ dword = idx / 32;
+ bit = idx % 32;
+ }
+
+ return m_dwords[dword] & (1u << bit);
+ }
+
+ constexpr void set(uint32_t idx, bool value) {
+ uint32_t dword = 0;
+ uint32_t bit = idx;
+
+ // Compiler doesn't remove this otherwise.
+ if constexpr (Dwords > 1) {
+ dword = idx / 32;
+ bit = idx % 32;
+ }
+
+ if (value)
+ m_dwords[dword] |= 1u << bit;
+ else
+ m_dwords[dword] &= ~(1u << bit);
+ }
+
+ constexpr bool exchange(uint32_t idx, bool value) {
+ bool oldValue = get(idx);
+ set(idx, value);
+ return oldValue;
+ }
+
+ constexpr void flip(uint32_t idx) {
+ uint32_t dword = 0;
+ uint32_t bit = idx;
+
+ // Compiler doesn't remove this otherwise.
+ if constexpr (Dwords > 1) {
+ dword = idx / 32;
+ bit = idx % 32;
+ }
+
+ m_dwords[dword] ^= 1u << bit;
+ }
+
+ constexpr void setAll() {
+ if constexpr (Bits % 32 == 0) {
+ for (size_t i = 0; i < Dwords; i++)
+ m_dwords[i] = std::numeric_limits<uint32_t>::max();
+ }
+ else {
+ for (size_t i = 0; i < Dwords - 1; i++)
+ m_dwords[i] = std::numeric_limits<uint32_t>::max();
+
+ m_dwords[Dwords - 1] = (1u << (Bits % 32)) - 1;
+ }
+ }
+
+ constexpr void clearAll() {
+ for (size_t i = 0; i < Dwords; i++)
+ m_dwords[i] = 0;
+ }
+
+ constexpr bool any() const {
+ for (size_t i = 0; i < Dwords; i++) {
+ if (m_dwords[i] != 0)
+ return true;
+ }
+
+ return false;
+ }
+
+ constexpr uint32_t& dword(uint32_t idx) {
+ return m_dwords[idx];
+ }
+
+ constexpr size_t bitCount() {
+ return Bits;
+ }
+
+ constexpr size_t dwordCount() {
+ return Dwords;
+ }
+
+ constexpr bool operator [] (uint32_t idx) const {
+ return get(idx);
+ }
+
+ private:
+
+ uint32_t m_dwords[Dwords];
+
+ };
+
+ class BitMask {
+
+ public:
+
+ class iterator: public std::iterator<std::input_iterator_tag,
+ uint32_t, uint32_t, const uint32_t*, uint32_t> {
+ public:
+
+ explicit iterator(uint32_t flags)
+ : m_mask(flags) { }
+
+ iterator& operator ++ () {
+ m_mask &= m_mask - 1;
+ return *this;
+ }
+
+ iterator operator ++ (int) {
+ iterator retval = *this;
+ m_mask &= m_mask - 1;
+ return retval;
+ }
+
+ uint32_t operator * () const {
+ return bsf(m_mask);
+ }
+
+ bool operator == (iterator other) const { return m_mask == other.m_mask; }
+ bool operator != (iterator other) const { return m_mask != other.m_mask; }
+
+ private:
+
+ uint32_t m_mask;
+
+ };
+
+ BitMask() { }
+
+ BitMask(uint32_t n)
+ : m_mask(n) { }
+
+ iterator begin() {
+ return iterator(m_mask);
+ }
+
+ iterator end() {
+ return iterator(0);
+ }
+
+ private:
+
+ uint32_t m_mask;
+
+ };
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_enum.h b/src/libs/dxvk-native-1.9.2a/src/util/util_enum.h
new file mode 100644
index 00000000..85b9b21b
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_enum.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#define ENUM_NAME(name) \
+ case name: return os << #name
+
+#define ENUM_DEFAULT(name) \
+ default: return os << static_cast<int32_t>(e)
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_env.cpp b/src/libs/dxvk-native-1.9.2a/src/util/util_env.cpp
new file mode 100644
index 00000000..3993ea65
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_env.cpp
@@ -0,0 +1,71 @@
+#include <numeric>
+
+#include "util_env.h"
+
+namespace dxvk::env {
+
+#ifndef DXVK_NATIVE
+ constexpr char dirSlash = '\\';
+#else
+ constexpr char dirSlash = '/';
+#endif
+
+
+ std::string getEnvVar(const char* name) {
+#ifdef DXVK_NATIVE
+ char* result = std::getenv(name);
+ return (result)
+ ? result
+ : "";
+#else
+ std::vector<WCHAR> result;
+ result.resize(MAX_PATH + 1);
+
+ DWORD len = ::GetEnvironmentVariableW(str::tows(name).c_str(), result.data(), MAX_PATH);
+ result.resize(len);
+
+ return str::fromws(result.data());
+#endif
+ }
+
+
+ size_t matchFileExtension(const std::string& name, const char* ext) {
+ auto pos = name.find_last_of('.');
+
+ if (pos == std::string::npos)
+ return pos;
+
+ bool matches = std::accumulate(name.begin() + pos + 1, name.end(), true,
+ [&ext] (bool current, char a) {
+ if (a >= 'A' && a <= 'Z')
+ a += 'a' - 'A';
+ return current && *ext && a == *(ext++);
+ });
+
+ return matches ? pos : std::string::npos;
+ }
+
+
+ std::string getExeName() {
+ std::string fullPath = getExePath();
+ auto n = fullPath.find_last_of(dirSlash);
+
+ return (n != std::string::npos)
+ ? fullPath.substr(n + 1)
+ : fullPath;
+ }
+
+
+ std::string getExeBaseName() {
+ auto exeName = getExeName();
+#ifndef DXVK_NATIVE
+ auto extp = matchFileExtension(exeName, "exe");
+
+ if (extp != std::string::npos)
+ exeName.erase(extp);
+#endif
+
+ return exeName;
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_env.h b/src/libs/dxvk-native-1.9.2a/src/util/util_env.h
new file mode 100644
index 00000000..cb4aa2a0
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_env.h
@@ -0,0 +1,73 @@
+#pragma once
+
+#include "util_string.h"
+
+namespace dxvk::env {
+
+ /**
+ * \brief Checks whether the host platform is 32-bit
+ */
+ constexpr bool is32BitHostPlatform() {
+ return sizeof(void*) == 4;
+ }
+
+ /**
+ * \brief Gets environment variable
+ *
+ * If the variable is not defined, this will return
+ * an empty string. Note that environment variables
+ * may be defined with an empty value.
+ * \param [in] name Name of the variable
+ * \returns Value of the variable
+ */
+ std::string getEnvVar(const char* name);
+
+ /**
+ * \brief Checks whether a file name has a given extension
+ *
+ * \param [in] name File name
+ * \param [in] ext Extension to match, in lowercase letters
+ * \returns Position of the extension within the file name, or
+ * \c std::string::npos if the file has a different extension
+ */
+ size_t matchFileExtension(const std::string& name, const char* ext);
+
+ /**
+ * \brief Gets the executable name
+ *
+ * Returns the base name (not the full path) of the
+ * program executable, including the file extension.
+ * This function should be used to identify programs.
+ * \returns Executable name
+ */
+ std::string getExeName();
+
+ /**
+ * \brief Gets the executable name without extension
+ *
+ * Same as \ref getExeName but without the file extension.
+ * \returns Executable name
+ */
+ std::string getExeBaseName();
+
+ /**
+ * \brief Gets full path to executable
+ * \returns Path to executable
+ */
+ std::string getExePath();
+
+ /**
+ * \brief Sets name of the calling thread
+ * \param [in] name Thread name
+ */
+ void setThreadName(const std::string& name);
+
+ /**
+ * \brief Creates a directory
+ *
+ * \param [in] path Path to directory
+ * \returns \c true on success
+ */
+ bool createDirectory(const std::string& path);
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_error.h b/src/libs/dxvk-native-1.9.2a/src/util/util_error.h
new file mode 100644
index 00000000..2cfd45ff
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_error.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <string>
+
+namespace dxvk {
+
+ /**
+ * \brief DXVK error
+ *
+ * A generic exception class that stores a
+ * message. Exceptions should be logged.
+ */
+ class DxvkError {
+
+ public:
+
+ DxvkError() { }
+ DxvkError(std::string&& message)
+ : m_message(std::move(message)) { }
+
+ const std::string& message() const {
+ return m_message;
+ }
+
+ private:
+
+ std::string m_message;
+
+ };
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_flags.h b/src/libs/dxvk-native-1.9.2a/src/util/util_flags.h
new file mode 100644
index 00000000..f67b4a2e
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_flags.h
@@ -0,0 +1,110 @@
+#pragma once
+
+#include <type_traits>
+
+#include "util_bit.h"
+
+namespace dxvk {
+
+ template<typename T>
+ class Flags {
+
+ public:
+
+ using IntType = std::underlying_type_t<T>;
+
+ Flags() { }
+
+ Flags(IntType t)
+ : m_bits(t) { }
+
+ template<typename... Tx>
+ Flags(T f, Tx... fx) {
+ this->set(f, fx...);
+ }
+
+ template<typename... Tx>
+ void set(Tx... fx) {
+ m_bits |= bits(fx...);
+ }
+
+ void set(Flags flags) {
+ m_bits |= flags.m_bits;
+ }
+
+ template<typename... Tx>
+ void clr(Tx... fx) {
+ m_bits &= ~bits(fx...);
+ }
+
+ void clr(Flags flags) {
+ m_bits &= ~flags.m_bits;
+ }
+
+ template<typename... Tx>
+ bool any(Tx... fx) const {
+ return (m_bits & bits(fx...)) != 0;
+ }
+
+ template<typename... Tx>
+ bool all(Tx... fx) const {
+ const IntType mask = bits(fx...);
+ return (m_bits & mask) == mask;
+ }
+
+ bool test(T f) const {
+ return this->any(f);
+ }
+
+ bool isClear() const {
+ return m_bits == 0;
+ }
+
+ void clrAll() {
+ m_bits = 0;
+ }
+
+ IntType raw() const {
+ return m_bits;
+ }
+
+ Flags operator & (const Flags& other) const {
+ return Flags(m_bits & other.m_bits);
+ }
+
+ Flags operator | (const Flags& other) const {
+ return Flags(m_bits | other.m_bits);
+ }
+
+ Flags operator ^ (const Flags& other) const {
+ return Flags(m_bits ^ other.m_bits);
+ }
+
+ bool operator == (const Flags& other) const {
+ return m_bits == other.m_bits;
+ }
+
+ bool operator != (const Flags& other) const {
+ return m_bits != other.m_bits;
+ }
+
+ private:
+
+ IntType m_bits = 0;
+
+ static IntType bit(T f) {
+ return IntType(1) << static_cast<IntType>(f);
+ }
+
+ template<typename... Tx>
+ static IntType bits(T f, Tx... fx) {
+ return bit(f) | bits(fx...);
+ }
+
+ static IntType bits() {
+ return 0;
+ }
+
+ };
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_fps_limiter.cpp b/src/libs/dxvk-native-1.9.2a/src/util/util_fps_limiter.cpp
new file mode 100644
index 00000000..5256c8d6
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_fps_limiter.cpp
@@ -0,0 +1,175 @@
+#include <thread>
+
+#include "thread.h"
+#include "util_env.h"
+#include "util_fps_limiter.h"
+#include "util_string.h"
+
+#include "./log/log.h"
+
+namespace dxvk {
+
+ FpsLimiter::FpsLimiter() {
+ std::string env = env::getEnvVar("DXVK_FRAME_RATE");
+
+ if (!env.empty()) {
+ try {
+ setTargetFrameRate(std::stod(env));
+ m_envOverride = true;
+ } catch (const std::invalid_argument&) {
+ // no-op
+ }
+ }
+ }
+
+
+ FpsLimiter::~FpsLimiter() {
+
+ }
+
+
+ void FpsLimiter::setTargetFrameRate(double frameRate) {
+ std::lock_guard<dxvk::mutex> lock(m_mutex);
+
+ if (!m_envOverride) {
+ m_targetInterval = frameRate > 0.0
+ ? NtTimerDuration(int64_t(double(NtTimerDuration::period::den) / frameRate))
+ : NtTimerDuration::zero();
+
+ if (isEnabled() && !m_initialized)
+ initialize();
+ }
+ }
+
+
+ void FpsLimiter::setDisplayRefreshRate(double refreshRate) {
+ std::lock_guard<dxvk::mutex> lock(m_mutex);
+
+ m_refreshInterval = refreshRate > 0.0
+ ? NtTimerDuration(int64_t(double(NtTimerDuration::period::den) / refreshRate))
+ : NtTimerDuration::zero();
+ }
+
+
+ void FpsLimiter::delay(bool vsyncEnabled) {
+ std::lock_guard<dxvk::mutex> lock(m_mutex);
+
+ if (!isEnabled())
+ return;
+
+ // If the swap chain is known to have vsync enabled and the
+ // refresh rate is similar to the target frame rate, disable
+ // the limiter so it does not screw up frame times
+ if (vsyncEnabled && !m_envOverride
+ && m_refreshInterval * 100 > m_targetInterval * 97)
+ return;
+
+ auto t0 = m_lastFrame;
+ auto t1 = dxvk::high_resolution_clock::now();
+
+ auto frameTime = std::chrono::duration_cast<NtTimerDuration>(t1 - t0);
+
+ if (frameTime * 100 > m_targetInterval * 103 - m_deviation * 100) {
+ // If we have a slow frame, reset the deviation since we
+ // do not want to compensate for low performance later on
+ m_deviation = NtTimerDuration::zero();
+ } else {
+ // Don't call sleep if the amount of time to sleep is shorter
+ // than the time the function calls are likely going to take
+ NtTimerDuration sleepDuration = m_targetInterval - m_deviation - frameTime;
+ t1 = sleep(t1, sleepDuration);
+
+ // Compensate for any sleep inaccuracies in the next frame, and
+ // limit cumulative deviation in order to avoid stutter in case we
+ // have a number of slow frames immediately followed by a fast one.
+ frameTime = std::chrono::duration_cast<NtTimerDuration>(t1 - t0);
+ m_deviation += frameTime - m_targetInterval;
+ m_deviation = std::min(m_deviation, m_targetInterval / 16);
+ }
+
+ m_lastFrame = t1;
+ }
+
+
+ FpsLimiter::TimePoint FpsLimiter::sleep(TimePoint t0, NtTimerDuration duration) {
+ if (duration <= NtTimerDuration::zero())
+ return t0;
+
+ // On wine, we can rely on NtDelayExecution waiting for more or
+ // less exactly the desired amount of time, and we want to avoid
+ // spamming QueryPerformanceCounter for performance reasons.
+ // On Windows, we busy-wait for the last couple of milliseconds
+ // since sleeping is highly inaccurate and inconsistent.
+ NtTimerDuration sleepThreshold = m_sleepThreshold;
+
+ if (m_sleepGranularity != NtTimerDuration::zero())
+ sleepThreshold += duration / 6;
+
+ NtTimerDuration remaining = duration;
+ TimePoint t1 = t0;
+
+ while (remaining > sleepThreshold) {
+ NtTimerDuration sleepDuration = remaining - sleepThreshold;
+
+ if (NtDelayExecution) {
+ LARGE_INTEGER ticks;
+ ticks.QuadPart = -sleepDuration.count();
+
+ NtDelayExecution(FALSE, &ticks);
+ } else {
+ std::this_thread::sleep_for(sleepDuration);
+ }
+
+ t1 = dxvk::high_resolution_clock::now();
+ remaining -= std::chrono::duration_cast<NtTimerDuration>(t1 - t0);
+ t0 = t1;
+ }
+
+ // Busy-wait until we have slept long enough
+ while (remaining > NtTimerDuration::zero()) {
+ t1 = dxvk::high_resolution_clock::now();
+ remaining -= std::chrono::duration_cast<NtTimerDuration>(t1 - t0);
+ t0 = t1;
+ }
+
+ return t1;
+ }
+
+
+ void FpsLimiter::initialize() {
+#ifdef _WIN32
+ HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
+
+ if (ntdll) {
+ NtDelayExecution = reinterpret_cast<NtDelayExecutionProc>(
+ ::GetProcAddress(ntdll, "NtDelayExecution"));
+ auto NtQueryTimerResolution = reinterpret_cast<NtQueryTimerResolutionProc>(
+ ::GetProcAddress(ntdll, "NtQueryTimerResolution"));
+ auto NtSetTimerResolution = reinterpret_cast<NtSetTimerResolutionProc>(
+ ::GetProcAddress(ntdll, "NtSetTimerResolution"));
+
+ ULONG min, max, cur;
+
+ // Wine's implementation of these functions is a stub as of 6.10, which is fine
+ // since it uses select() in NtDelayExecution. This is only relevant for Windows.
+ if (NtQueryTimerResolution && !NtQueryTimerResolution(&min, &max, &cur)) {
+ m_sleepGranularity = NtTimerDuration(cur);
+
+ if (NtSetTimerResolution && !NtSetTimerResolution(max, TRUE, &cur)) {
+ Logger::info(str::format("Setting timer interval to ", (double(max) / 10.0), " us"));
+ m_sleepGranularity = NtTimerDuration(max);
+ }
+ }
+ } else
+#endif
+ {
+ // Assume 1ms sleep granularity by default
+ m_sleepGranularity = NtTimerDuration(10000);
+ }
+
+ m_sleepThreshold = 4 * m_sleepGranularity;
+ m_lastFrame = dxvk::high_resolution_clock::now();
+ m_initialized = true;
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_fps_limiter.h b/src/libs/dxvk-native-1.9.2a/src/util/util_fps_limiter.h
new file mode 100644
index 00000000..9601dc96
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_fps_limiter.h
@@ -0,0 +1,89 @@
+#pragma once
+
+#include "thread.h"
+#include "util_time.h"
+
+namespace dxvk {
+
+ /**
+ * \brief Frame rate limiter
+ *
+ * Provides functionality to stall an application
+ * thread in order to maintain a given frame rate.
+ */
+ class FpsLimiter {
+
+ public:
+
+ /**
+ * \brief Creates frame rate limiter
+ */
+ FpsLimiter();
+
+ ~FpsLimiter();
+
+ /**
+ * \brief Sets target frame rate
+ * \param [in] frameRate Target frame rate
+ */
+ void setTargetFrameRate(double frameRate);
+
+ /**
+ * \brief Sets display refresh rate
+ *
+ * This information is used to decide whether or not
+ * the limiter should be active in the first place in
+ * case vertical synchronization is enabled.
+ * \param [in] refreshRate Current refresh rate
+ */
+ void setDisplayRefreshRate(double refreshRate);
+
+ /**
+ * \brief Stalls calling thread as necessary
+ *
+ * Blocks the calling thread if the limiter is enabled
+ * and the time since the last call to \ref delay is
+ * shorter than the target interval.
+ * \param [in] vsyncEnabled \c true if vsync is enabled
+ */
+ void delay(bool vsyncEnabled);
+
+ /**
+ * \brief Checks whether the frame rate limiter is enabled
+ * \returns \c true if the target frame rate is non-zero.
+ */
+ bool isEnabled() const {
+ return m_targetInterval != NtTimerDuration::zero();
+ }
+
+ private:
+
+ using TimePoint = dxvk::high_resolution_clock::time_point;
+
+ using NtTimerDuration = std::chrono::duration<int64_t, std::ratio<1, 10000000>>;
+ using NtQueryTimerResolutionProc = UINT (WINAPI *) (ULONG*, ULONG*, ULONG*);
+ using NtSetTimerResolutionProc = UINT (WINAPI *) (ULONG, BOOL, ULONG*);
+ using NtDelayExecutionProc = UINT (WINAPI *) (BOOL, LARGE_INTEGER*);
+
+ dxvk::mutex m_mutex;
+
+ NtTimerDuration m_targetInterval = NtTimerDuration::zero();
+ NtTimerDuration m_refreshInterval = NtTimerDuration::zero();
+ NtTimerDuration m_deviation = NtTimerDuration::zero();
+ TimePoint m_lastFrame;
+
+ bool m_initialized = false;
+ bool m_envOverride = false;
+
+ NtTimerDuration m_sleepGranularity = NtTimerDuration::zero();
+ NtTimerDuration m_sleepThreshold = NtTimerDuration::zero();
+
+ NtDelayExecutionProc NtDelayExecution = nullptr;
+
+ TimePoint sleep(TimePoint t0, NtTimerDuration duration);
+
+ void initialize();
+
+ };
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_gdi.cpp b/src/libs/dxvk-native-1.9.2a/src/util/util_gdi.cpp
new file mode 100644
index 00000000..901dd05c
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_gdi.cpp
@@ -0,0 +1,33 @@
+#include "util_gdi.h"
+#include "log/log.h"
+
+namespace dxvk {
+
+ HMODULE GetGDIModule() {
+ static HMODULE module = LoadLibraryA("gdi32.dll");
+ return module;
+ }
+
+ NTSTATUS D3DKMTCreateDCFromMemory(D3DKMT_CREATEDCFROMMEMORY* Arg1) {
+ static auto func = (D3DKMTCreateDCFromMemoryType)
+ GetProcAddress(GetGDIModule(), "D3DKMTCreateDCFromMemory");
+
+ if (func != nullptr)
+ return func(Arg1);
+
+ Logger::warn("D3DKMTCreateDCFromMemory: Unable to query proc address.");
+ return -1;
+ }
+
+ NTSTATUS D3DKMTDestroyDCFromMemory(D3DKMT_DESTROYDCFROMMEMORY* Arg1) {
+ static auto func = (D3DKMTDestroyDCFromMemoryType)
+ GetProcAddress(GetGDIModule(), "D3DKMTDestroyDCFromMemory");
+
+ if (func != nullptr)
+ return func(Arg1);
+
+ Logger::warn("D3DKMTDestroyDCFromMemory: Unable to query proc address.");
+ return -1;
+ }
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_gdi.h b/src/libs/dxvk-native-1.9.2a/src/util/util_gdi.h
new file mode 100644
index 00000000..58724b15
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_gdi.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <d3d9.h>
+
+namespace dxvk {
+ using NTSTATUS = LONG;
+
+ // Slightly modified definitions...
+ struct D3DKMT_CREATEDCFROMMEMORY {
+ void* pMemory;
+ D3DFORMAT Format;
+ UINT Width;
+ UINT Height;
+ UINT Pitch;
+ HDC hDeviceDc;
+ PALETTEENTRY* pColorTable;
+ HDC hDc;
+ HANDLE hBitmap;
+ };
+
+ struct D3DKMT_DESTROYDCFROMMEMORY {
+ HDC hDC = nullptr;
+ HANDLE hBitmap = nullptr;
+ };
+
+ using D3DKMTCreateDCFromMemoryType = NTSTATUS(STDMETHODCALLTYPE*) (D3DKMT_CREATEDCFROMMEMORY*);
+ NTSTATUS D3DKMTCreateDCFromMemory (D3DKMT_CREATEDCFROMMEMORY* Arg1);
+
+ using D3DKMTDestroyDCFromMemoryType = NTSTATUS(STDMETHODCALLTYPE*) (D3DKMT_DESTROYDCFROMMEMORY*);
+ NTSTATUS D3DKMTDestroyDCFromMemory(D3DKMT_DESTROYDCFROMMEMORY* Arg1);
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_lazy.h b/src/libs/dxvk-native-1.9.2a/src/util/util_lazy.h
new file mode 100644
index 00000000..f17febb8
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_lazy.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <mutex>
+
+namespace dxvk {
+
+ /**
+ * \brief Lazy-initialized object
+ *
+ * Creates an object on demand with
+ * the given constructor arguments.
+ */
+ template<typename T>
+ class Lazy {
+
+ public:
+
+ template<typename... Args>
+ T& get(Args... args) {
+ if (m_object)
+ return *m_object;
+
+ std::lock_guard lock(m_mutex);
+
+ if (!m_object) {
+ m_object = std::make_unique<T>(
+ std::forward<Args>(args)...);
+ }
+
+ return *m_object;
+ }
+
+ private:
+
+ dxvk::mutex m_mutex;
+ std::unique_ptr<T> m_object;
+
+ };
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_likely.h b/src/libs/dxvk-native-1.9.2a/src/util/util_likely.h
new file mode 100644
index 00000000..7eba9818
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_likely.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#ifdef __GNUC__
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
+#else
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_luid.h b/src/libs/dxvk-native-1.9.2a/src/util/util_luid.h
new file mode 100644
index 00000000..718040e3
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_luid.h
@@ -0,0 +1,15 @@
+#include "./com/com_include.h"
+
+namespace dxvk {
+
+ /**
+ * \brief Retrieves an adapter LUID
+ *
+ * Note that this only works reliably within the
+ * module that this function was compiled into.
+ * \param [in] Adapter The adapter index
+ * \returns LUID An LUID for that adapter
+ */
+ LUID GetAdapterLUID(UINT Adapter);
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_math.h b/src/libs/dxvk-native-1.9.2a/src/util/util_math.h
new file mode 100644
index 00000000..fdb3762b
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_math.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <cmath>
+
+namespace dxvk {
+
+ constexpr size_t CACHE_LINE_SIZE = 64;
+
+ template<typename T>
+ constexpr T clamp(T n, T lo, T hi) {
+ if (n < lo) return lo;
+ if (n > hi) return hi;
+ return n;
+ }
+
+ template<typename T, typename U = T>
+ constexpr T align(T what, U to) {
+ return (what + to - 1) & ~(to - 1);
+ }
+
+ template<typename T, typename U = T>
+ constexpr T alignDown(T what, U to) {
+ return (what / to) * to;
+ }
+
+ // Equivalent of std::clamp for use with floating point numbers
+ // Handles (-){INFINITY,NAN} cases.
+ // Will return min in cases of NAN, etc.
+ inline float fclamp(float value, float min, float max) {
+ return std::fmin(
+ std::fmax(value, min), max);
+ }
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_matrix.cpp b/src/libs/dxvk-native-1.9.2a/src/util/util_matrix.cpp
new file mode 100644
index 00000000..2c5e9314
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_matrix.cpp
@@ -0,0 +1,232 @@
+#include "util_matrix.h"
+
+namespace dxvk {
+
+ Vector4& Matrix4::operator[](size_t index) { return data[index]; }
+ const Vector4& Matrix4::operator[](size_t index) const { return data[index]; }
+
+ bool Matrix4::operator==(const Matrix4& m2) const {
+ const Matrix4& m1 = *this;
+ for (uint32_t i = 0; i < 4; i++) {
+ if (m1[i] != m2[i])
+ return false;
+ }
+ return true;
+ }
+
+ bool Matrix4::operator!=(const Matrix4& m2) const { return !operator==(m2); }
+
+ Matrix4 Matrix4::operator+(const Matrix4& other) const {
+ Matrix4 mat;
+ for (uint32_t i = 0; i < 4; i++)
+ mat[i] = data[i] + other.data[i];
+ return mat;
+ }
+
+ Matrix4 Matrix4::operator-(const Matrix4& other) const {
+ Matrix4 mat;
+ for (uint32_t i = 0; i < 4; i++)
+ mat[i] = data[i] - other.data[i];
+ return mat;
+ }
+
+ Matrix4 Matrix4::operator*(const Matrix4& m2) const {
+ const Matrix4& m1 = *this;
+
+ const Vector4 srcA0 = { m1[0] };
+ const Vector4 srcA1 = { m1[1] };
+ const Vector4 srcA2 = { m1[2] };
+ const Vector4 srcA3 = { m1[3] };
+
+ const Vector4 srcB0 = { m2[0] };
+ const Vector4 srcB1 = { m2[1] };
+ const Vector4 srcB2 = { m2[2] };
+ const Vector4 srcB3 = { m2[3] };
+
+ Matrix4 result;
+ result[0] = srcA0 * srcB0[0] + srcA1 * srcB0[1] + srcA2 * srcB0[2] + srcA3 * srcB0[3];
+ result[1] = srcA0 * srcB1[0] + srcA1 * srcB1[1] + srcA2 * srcB1[2] + srcA3 * srcB1[3];
+ result[2] = srcA0 * srcB2[0] + srcA1 * srcB2[1] + srcA2 * srcB2[2] + srcA3 * srcB2[3];
+ result[3] = srcA0 * srcB3[0] + srcA1 * srcB3[1] + srcA2 * srcB3[2] + srcA3 * srcB3[3];
+ return result;
+ }
+
+ Vector4 Matrix4::operator*(const Vector4& v) const {
+ const Matrix4& m = *this;
+
+ const Vector4 mul0 = { m[0] * v[0] };
+ const Vector4 mul1 = { m[1] * v[1] };
+ const Vector4 mul2 = { m[2] * v[2] };
+ const Vector4 mul3 = { m[3] * v[3] };
+
+ const Vector4 add0 = { mul0 + mul1 };
+ const Vector4 add1 = { mul2 + mul3 };
+
+ return add0 + add1;
+ }
+
+ Matrix4 Matrix4::operator*(float scalar) const {
+ Matrix4 mat;
+ for (uint32_t i = 0; i < 4; i++)
+ mat[i] = data[i] * scalar;
+ return mat;
+ }
+
+ Matrix4 Matrix4::operator/(float scalar) const {
+ Matrix4 mat;
+ for (uint32_t i = 0; i < 4; i++)
+ mat[i] = data[i] / scalar;
+ return mat;
+ }
+
+ Matrix4& Matrix4::operator+=(const Matrix4& other) {
+ for (uint32_t i = 0; i < 4; i++)
+ data[i] += other.data[i];
+ return *this;
+ }
+
+ Matrix4& Matrix4::operator-=(const Matrix4& other) {
+ for (uint32_t i = 0; i < 4; i++)
+ data[i] -= other.data[i];
+ return *this;
+ }
+
+ Matrix4& Matrix4::operator*=(const Matrix4& other) {
+ return (*this = (*this) * other);
+ }
+
+ Matrix4 transpose(const Matrix4& m) {
+ Matrix4 result;
+
+ for (uint32_t i = 0; i < 4; i++) {
+ for (uint32_t j = 0; j < 4; j++)
+ result[i][j] = m.data[j][i];
+ }
+ return result;
+ }
+
+ float determinant(const Matrix4& m) {
+ float coef00 = m[2][2] * m[3][3] - m[3][2] * m[2][3];
+ float coef02 = m[1][2] * m[3][3] - m[3][2] * m[1][3];
+ float coef03 = m[1][2] * m[2][3] - m[2][2] * m[1][3];
+
+ float coef04 = m[2][1] * m[3][3] - m[3][1] * m[2][3];
+ float coef06 = m[1][1] * m[3][3] - m[3][1] * m[1][3];
+ float coef07 = m[1][1] * m[2][3] - m[2][1] * m[1][3];
+
+ float coef08 = m[2][1] * m[3][2] - m[3][1] * m[2][2];
+ float coef10 = m[1][1] * m[3][2] - m[3][1] * m[1][2];
+ float coef11 = m[1][1] * m[2][2] - m[2][1] * m[1][2];
+
+ float coef12 = m[2][0] * m[3][3] - m[3][0] * m[2][3];
+ float coef14 = m[1][0] * m[3][3] - m[3][0] * m[1][3];
+ float coef15 = m[1][0] * m[2][3] - m[2][0] * m[1][3];
+
+ float coef16 = m[2][0] * m[3][2] - m[3][0] * m[2][2];
+ float coef18 = m[1][0] * m[3][2] - m[3][0] * m[1][2];
+ float coef19 = m[1][0] * m[2][2] - m[2][0] * m[1][2];
+
+ float coef20 = m[2][0] * m[3][1] - m[3][0] * m[2][1];
+ float coef22 = m[1][0] * m[3][1] - m[3][0] * m[1][1];
+ float coef23 = m[1][0] * m[2][1] - m[2][0] * m[1][1];
+
+ Vector4 fac0 = { coef00, coef00, coef02, coef03 };
+ Vector4 fac1 = { coef04, coef04, coef06, coef07 };
+ Vector4 fac2 = { coef08, coef08, coef10, coef11 };
+ Vector4 fac3 = { coef12, coef12, coef14, coef15 };
+ Vector4 fac4 = { coef16, coef16, coef18, coef19 };
+ Vector4 fac5 = { coef20, coef20, coef22, coef23 };
+
+ Vector4 vec0 = { m[1][0], m[0][0], m[0][0], m[0][0] };
+ Vector4 vec1 = { m[1][1], m[0][1], m[0][1], m[0][1] };
+ Vector4 vec2 = { m[1][2], m[0][2], m[0][2], m[0][2] };
+ Vector4 vec3 = { m[1][3], m[0][3], m[0][3], m[0][3] };
+
+ Vector4 inv0 = { vec1 * fac0 - vec2 * fac1 + vec3 * fac2 };
+ Vector4 inv1 = { vec0 * fac0 - vec2 * fac3 + vec3 * fac4 };
+ Vector4 inv2 = { vec0 * fac1 - vec1 * fac3 + vec3 * fac5 };
+ Vector4 inv3 = { vec0 * fac2 - vec1 * fac4 + vec2 * fac5 };
+
+ Vector4 signA = { +1, -1, +1, -1 };
+ Vector4 signB = { -1, +1, -1, +1 };
+ Matrix4 inverse = { inv0 * signA, inv1 * signB, inv2 * signA, inv3 * signB };
+
+ Vector4 row0 = { inverse[0][0], inverse[1][0], inverse[2][0], inverse[3][0] };
+
+ Vector4 dot0 = { m[0] * row0 };
+
+ return (dot0.x + dot0.y) + (dot0.z + dot0.w);
+ }
+
+ Matrix4 inverse(const Matrix4& m)
+ {
+ float coef00 = m[2][2] * m[3][3] - m[3][2] * m[2][3];
+ float coef02 = m[1][2] * m[3][3] - m[3][2] * m[1][3];
+ float coef03 = m[1][2] * m[2][3] - m[2][2] * m[1][3];
+ float coef04 = m[2][1] * m[3][3] - m[3][1] * m[2][3];
+ float coef06 = m[1][1] * m[3][3] - m[3][1] * m[1][3];
+ float coef07 = m[1][1] * m[2][3] - m[2][1] * m[1][3];
+ float coef08 = m[2][1] * m[3][2] - m[3][1] * m[2][2];
+ float coef10 = m[1][1] * m[3][2] - m[3][1] * m[1][2];
+ float coef11 = m[1][1] * m[2][2] - m[2][1] * m[1][2];
+ float coef12 = m[2][0] * m[3][3] - m[3][0] * m[2][3];
+ float coef14 = m[1][0] * m[3][3] - m[3][0] * m[1][3];
+ float coef15 = m[1][0] * m[2][3] - m[2][0] * m[1][3];
+ float coef16 = m[2][0] * m[3][2] - m[3][0] * m[2][2];
+ float coef18 = m[1][0] * m[3][2] - m[3][0] * m[1][2];
+ float coef19 = m[1][0] * m[2][2] - m[2][0] * m[1][2];
+ float coef20 = m[2][0] * m[3][1] - m[3][0] * m[2][1];
+ float coef22 = m[1][0] * m[3][1] - m[3][0] * m[1][1];
+ float coef23 = m[1][0] * m[2][1] - m[2][0] * m[1][1];
+
+ Vector4 fac0 = { coef00, coef00, coef02, coef03 };
+ Vector4 fac1 = { coef04, coef04, coef06, coef07 };
+ Vector4 fac2 = { coef08, coef08, coef10, coef11 };
+ Vector4 fac3 = { coef12, coef12, coef14, coef15 };
+ Vector4 fac4 = { coef16, coef16, coef18, coef19 };
+ Vector4 fac5 = { coef20, coef20, coef22, coef23 };
+
+ Vector4 vec0 = { m[1][0], m[0][0], m[0][0], m[0][0] };
+ Vector4 vec1 = { m[1][1], m[0][1], m[0][1], m[0][1] };
+ Vector4 vec2 = { m[1][2], m[0][2], m[0][2], m[0][2] };
+ Vector4 vec3 = { m[1][3], m[0][3], m[0][3], m[0][3] };
+
+ Vector4 inv0 = { vec1 * fac0 - vec2 * fac1 + vec3 * fac2 };
+ Vector4 inv1 = { vec0 * fac0 - vec2 * fac3 + vec3 * fac4 };
+ Vector4 inv2 = { vec0 * fac1 - vec1 * fac3 + vec3 * fac5 };
+ Vector4 inv3 = { vec0 * fac2 - vec1 * fac4 + vec2 * fac5 };
+
+ Vector4 signA = { +1, -1, +1, -1 };
+ Vector4 signB = { -1, +1, -1, +1 };
+ Matrix4 inverse = { inv0 * signA, inv1 * signB, inv2 * signA, inv3 * signB };
+
+ Vector4 row0 = { inverse[0][0], inverse[1][0], inverse[2][0], inverse[3][0] };
+
+ Vector4 dot0 = { m[0] * row0 };
+ float dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w);
+
+ return inverse * (1.0f / dot1);
+ }
+
+ Matrix4 hadamardProduct(const Matrix4& a, const Matrix4& b) {
+ Matrix4 result;
+
+ for (uint32_t i = 0; i < 4; i++)
+ result[i] = a[i] * b[i];
+
+ return result;
+ }
+
+ std::ostream& operator<<(std::ostream& os, const Matrix4& m) {
+ os << "Matrix4(";
+ for (uint32_t i = 0; i < 4; i++) {
+ os << "\n\t" << m[i];
+ if (i < 3)
+ os << ", ";
+ }
+ os << "\n)";
+
+ return os;
+ }
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_matrix.h b/src/libs/dxvk-native-1.9.2a/src/util/util_matrix.h
new file mode 100644
index 00000000..98f260f8
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_matrix.h
@@ -0,0 +1,85 @@
+#pragma once
+
+#include "util_vector.h"
+
+namespace dxvk {
+
+ class Matrix4 {
+
+ public:
+
+ // Identity
+ inline Matrix4() {
+ data[0] = { 1, 0, 0, 0 };
+ data[1] = { 0, 1, 0, 0 };
+ data[2] = { 0, 0, 1, 0 };
+ data[3] = { 0, 0, 0, 1 };
+ }
+
+ // Produces a scalar matrix, x * Identity
+ inline explicit Matrix4(float x) {
+ data[0] = { x, 0, 0, 0 };
+ data[1] = { 0, x, 0, 0 };
+ data[2] = { 0, 0, x, 0 };
+ data[3] = { 0, 0, 0, x };
+ }
+
+ inline Matrix4(
+ const Vector4& v0,
+ const Vector4& v1,
+ const Vector4& v2,
+ const Vector4& v3) {
+ data[0] = v0;
+ data[1] = v1;
+ data[2] = v2;
+ data[3] = v3;
+ }
+
+ inline Matrix4(const float matrix[4][4]) {
+ data[0] = Vector4(matrix[0]);
+ data[1] = Vector4(matrix[1]);
+ data[2] = Vector4(matrix[2]);
+ data[3] = Vector4(matrix[3]);
+ }
+
+ Matrix4(const Matrix4& other) = default;
+
+ Vector4& operator[](size_t index);
+ const Vector4& operator[](size_t index) const;
+
+ bool operator==(const Matrix4& m2) const;
+ bool operator!=(const Matrix4& m2) const;
+
+ Matrix4 operator+(const Matrix4& other) const;
+ Matrix4 operator-(const Matrix4& other) const;
+
+ Matrix4 operator*(const Matrix4& m2) const;
+ Vector4 operator*(const Vector4& v) const;
+ Matrix4 operator*(float scalar) const;
+
+ Matrix4 operator/(float scalar) const;
+
+ Matrix4& operator+=(const Matrix4& other);
+ Matrix4& operator-=(const Matrix4& other);
+
+ Matrix4& operator*=(const Matrix4& other);
+
+ Vector4 data[4];
+
+ };
+
+ static_assert(sizeof(Matrix4) == sizeof(Vector4) * 4);
+
+ inline Matrix4 operator*(float scalar, const Matrix4& m) { return m * scalar; }
+
+ Matrix4 transpose(const Matrix4& m);
+
+ float determinant(const Matrix4& m);
+
+ Matrix4 inverse(const Matrix4& m);
+
+ Matrix4 hadamardProduct(const Matrix4& a, const Matrix4& b);
+
+ std::ostream& operator<<(std::ostream& os, const Matrix4& m);
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_monitor.cpp b/src/libs/dxvk-native-1.9.2a/src/util/util_monitor.cpp
new file mode 100644
index 00000000..7e1b7c6d
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_monitor.cpp
@@ -0,0 +1,51 @@
+#include "util_monitor.h"
+#include "util_string.h"
+
+#include "../wsi/wsi_mode.h"
+#include "../wsi/wsi_monitor.h"
+#include "../wsi/wsi_window.h"
+
+#include "./log/log.h"
+
+namespace dxvk {
+
+ HMONITOR GetDefaultMonitor() {
+ return wsi::getDefaultMonitor();
+ }
+
+
+ void GetWindowClientSize(
+ HWND hWnd,
+ UINT* pWidth,
+ UINT* pHeight) {
+ wsi::getWindowSize(hWnd, pWidth, pHeight);
+ }
+
+
+ void GetMonitorClientSize(
+ HMONITOR hMonitor,
+ UINT* pWidth,
+ UINT* pHeight) {
+ RECT rect;
+
+ if (!wsi::getDesktopCoordinates(hMonitor, &rect)) {
+ Logger::err("D3D9: Failed to query monitor info");
+ return;
+ }
+
+ if (pWidth)
+ *pWidth = rect.right - rect.left;
+
+ if (pHeight)
+ *pHeight = rect.bottom - rect.top;
+ }
+
+
+ void GetMonitorRect(
+ HMONITOR hMonitor,
+ RECT* pRect) {
+ if (!wsi::getDesktopCoordinates(hMonitor, pRect))
+ Logger::err("D3D9: Failed to query monitor info");
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_monitor.h b/src/libs/dxvk-native-1.9.2a/src/util/util_monitor.h
new file mode 100644
index 00000000..81d0a587
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_monitor.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "./com/com_include.h"
+
+namespace dxvk {
+
+ /**
+ * \brief Retrieves primary monitor
+ * \returns The primary monitor
+ */
+ HMONITOR GetDefaultMonitor();
+
+ /**
+ * \brief Queries window client size
+ *
+ * \param [in] hWnd Window to query
+ * \param [out] pWidth Client width
+ * \param [out] pHeight Client height
+ */
+ void GetWindowClientSize(
+ HWND hWnd,
+ UINT* pWidth,
+ UINT* pHeight);
+
+ /**
+ * \brief Queries monitor size
+ *
+ * \param [in] hMonitor Monitor to query
+ * \param [out] pWidth Client width
+ * \param [out] pHeight Client height
+ */
+ void GetMonitorClientSize(
+ HMONITOR hMonitor,
+ UINT* pWidth,
+ UINT* pHeight);
+
+ /**
+ * \brief Queries monitor rect
+ *
+ * \param [in] hMonitor Monitor to query
+ * \param [out] pRect The rect to return
+ */
+ void GetMonitorRect(
+ HMONITOR hMonitor,
+ RECT* pRect);
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_ratio.h b/src/libs/dxvk-native-1.9.2a/src/util/util_ratio.h
new file mode 100644
index 00000000..0257ad99
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_ratio.h
@@ -0,0 +1,88 @@
+#include <numeric>
+#include <algorithm>
+#include <cstdint>
+#include <string>
+#include <charconv>
+
+namespace dxvk {
+
+ /**
+ * \brief Simplest ratio helper
+ */
+ template <typename T>
+ class Ratio {
+
+ public:
+
+ Ratio(T num, T denom) {
+ set(num, denom);
+ }
+
+ Ratio(std::string_view view) {
+ set(0, 0);
+
+ size_t colon = view.find(":");
+
+ if (colon == std::string_view::npos)
+ return;
+
+ std::string_view numStr = view.substr(0, colon);
+ std::string_view denomStr = view.substr(colon + 1);
+
+ T num = 0, denom = 0;
+ std::from_chars(numStr.data(), numStr.data() + numStr.size(), num);
+ std::from_chars(denomStr.data(), denomStr.data() + denomStr.size(), denom);
+
+ set(num, denom);
+ }
+
+ inline T num() const { return m_num; }
+ inline T denom() const { return m_denom; }
+
+ inline bool undefined() const { return m_denom == 0; }
+
+ inline void set(T num, T denom) {
+ const T gcd = std::gcd(num, denom);
+
+ if (gcd == 0) {
+ m_num = 0;
+ m_denom = 0;
+
+ return;
+ }
+
+ m_num = num / gcd;
+ m_denom = denom / gcd;
+ }
+
+ inline bool operator == (const Ratio& other) const {
+ return num() == other.num() && denom() == other.denom();
+ }
+
+ inline bool operator != (const Ratio& other) const {
+ return !(*this == other);
+ }
+
+ inline bool operator >= (const Ratio& other) const {
+ return num() * other.denom() >= other.num() * denom();
+ }
+
+ inline bool operator > (const Ratio& other) const {
+ return num() * other.denom() > other.num() * denom();
+ }
+
+ inline bool operator < (const Ratio& other) const {
+ return !(*this >= other);
+ }
+
+ inline bool operator <= (const Ratio& other) const {
+ return !(*this > other);
+ }
+
+ private:
+
+ T m_num, m_denom;
+
+ };
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_small_vector.h b/src/libs/dxvk-native-1.9.2a/src/util/util_small_vector.h
new file mode 100644
index 00000000..c313ca4b
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_small_vector.h
@@ -0,0 +1,133 @@
+#pragma once
+
+#include <type_traits>
+
+namespace dxvk {
+
+ template<typename T, size_t N>
+ class small_vector {
+ using storage = std::aligned_storage_t<sizeof(T), alignof(T)>;
+ public:
+
+ small_vector() { }
+
+ small_vector (const small_vector&) = delete;
+ small_vector& operator = (const small_vector&) = delete;
+
+ ~small_vector() {
+ for (size_t i = 0; i < m_size; i++)
+ ptr(i)->~T();
+
+ if (m_capacity > N)
+ delete[] u.m_ptr;
+ }
+
+ size_t size() const {
+ return m_size;
+ }
+
+ void reserve(size_t n) {
+ n = pick_capacity(n);
+
+ if (n <= m_capacity)
+ return;
+
+ storage* data = new storage[n];
+
+ for (size_t i = 0; i < m_size; i++) {
+ new (&data[i]) T(std::move(*ptr(i)));
+ ptr(i)->~T();
+ }
+
+ if (m_capacity > N)
+ delete[] u.m_ptr;
+
+ m_capacity = n;
+ u.m_ptr = data;
+ }
+
+ const T* data() const { return ptr(0); }
+ T* data() { return ptr(0); }
+
+ void resize(size_t n) {
+ reserve(n);
+
+ for (size_t i = n; i < m_size; i++)
+ ptr(i)->~T();
+
+ for (size_t i = m_size; i < n; i++)
+ new (ptr(i)) T();
+ }
+
+ void push_back(const T& object) {
+ reserve(m_size + 1);
+ new (ptr(m_size++)) T(object);
+ }
+
+ void push_back(T&& object) {
+ reserve(m_size + 1);
+ new (ptr(m_size++)) T(std::move(object));
+ }
+
+ template<typename... Args>
+ void emplace_back(Args... args) {
+ reserve(m_size + 1);
+ new (ptr(m_size++)) T(std::forward<Args>(args)...);
+ }
+
+ void erase(size_t idx) {
+ ptr(idx)->~T();
+
+ for (size_t i = idx; i < m_size - 1; i++) {
+ new (ptr(i)) T(std::move(*ptr(i + 1)));
+ ptr(i + 1)->~T();
+ }
+ }
+
+ void pop_back() {
+ ptr(--m_size)->~T();
+ }
+
+ T& operator [] (size_t idx) { return *ptr(idx); }
+ const T& operator [] (size_t idx) const { return *ptr(idx); }
+
+ T& front() { return *ptr(0); }
+ const T& front() const { return *ptr(0); }
+
+ T& back() { return *ptr(m_size - 1); }
+ const T& back() const { return *ptr(m_size - 1); }
+
+ private:
+
+ size_t m_capacity = N;
+ size_t m_size = 0;
+
+ union {
+ storage* m_ptr;
+ storage m_data[sizeof(T) * N];
+ } u;
+
+ size_t pick_capacity(size_t n) {
+ size_t capacity = m_capacity;
+
+ while (capacity < n)
+ capacity *= 2;
+
+ return capacity;
+ }
+
+ T* ptr(size_t idx) {
+ return m_capacity == N
+ ? reinterpret_cast<T*>(&u.m_data[idx])
+ : reinterpret_cast<T*>(&u.m_ptr[idx]);
+ }
+
+ const T* ptr(size_t idx) const {
+ return m_capacity == N
+ ? reinterpret_cast<const T*>(&u.m_data[idx])
+ : reinterpret_cast<const T*>(&u.m_ptr[idx]);
+ }
+
+ };
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_string.h b/src/libs/dxvk-native-1.9.2a/src/util/util_string.h
new file mode 100644
index 00000000..cd738477
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_string.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "./com/com_include.h"
+
+namespace dxvk::str {
+
+ std::string fromws(const WCHAR *ws);
+
+ void tows(const char* mbs, WCHAR* wcs, size_t wcsLen);
+
+ template <size_t N>
+ void tows(const char* mbs, WCHAR (&wcs)[N]) {
+ return tows(mbs, wcs, N);
+ }
+
+ std::wstring tows(const char* mbs);
+
+ inline void format1(std::stringstream&) { }
+
+ template<typename... Tx>
+ void format1(std::stringstream& str, const WCHAR *arg, const Tx&... args) {
+ str << fromws(arg);
+ format1(str, args...);
+ }
+
+ template<typename T, typename... Tx>
+ void format1(std::stringstream& str, const T& arg, const Tx&... args) {
+ str << arg;
+ format1(str, args...);
+ }
+
+ template<typename... Args>
+ std::string format(const Args&... args) {
+ std::stringstream stream;
+ format1(stream, args...);
+ return stream.str();
+ }
+
+}
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_time.h b/src/libs/dxvk-native-1.9.2a/src/util/util_time.h
new file mode 100644
index 00000000..cbadfa5d
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_time.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <chrono>
+#include <cstdint>
+
+#if defined(_WIN32) && !defined(__WINE__)
+#include <windows.h>
+#endif
+
+namespace dxvk {
+
+#if defined(_WIN32) && !defined(__WINE__)
+ struct high_resolution_clock {
+ static constexpr bool is_steady = true;
+
+ using rep = int64_t;
+ using period = std::nano;
+ using duration = std::chrono::nanoseconds;
+ using time_point = std::chrono::time_point<high_resolution_clock>;
+
+ static inline time_point now() noexcept {
+ // Keep the frequency static, this doesn't change at all.
+ static const int64_t freq = getFrequency();
+ const int64_t counter = getCounter();
+
+ const int64_t whole = (counter / freq) * period::den;
+ const int64_t part = (counter % freq) * period::den / freq;
+
+ return time_point(duration(whole + part));
+ }
+
+ static inline int64_t getFrequency() {
+ LARGE_INTEGER freq;
+ QueryPerformanceFrequency(&freq);
+
+ return freq.QuadPart;
+ }
+
+ static inline int64_t getCounter() {
+ LARGE_INTEGER count;
+ QueryPerformanceCounter(&count);
+
+ return count.QuadPart;
+ }
+ };
+#else
+ using high_resolution_clock = std::chrono::high_resolution_clock;
+#endif
+
+} \ No newline at end of file
diff --git a/src/libs/dxvk-native-1.9.2a/src/util/util_vector.h b/src/libs/dxvk-native-1.9.2a/src/util/util_vector.h
new file mode 100644
index 00000000..77cdf294
--- /dev/null
+++ b/src/libs/dxvk-native-1.9.2a/src/util/util_vector.h
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <iostream>
+
+#include "util_bit.h"
+#include "util_math.h"
+
+namespace dxvk {
+
+ template <typename T>
+ struct Vector4Base {
+ Vector4Base()
+ : x{ }, y{ }, z{ }, w{ } { }
+
+ Vector4Base(T splat)
+ : x(splat), y(splat), z(splat), w(splat) { }
+
+ Vector4Base(T x, T y, T z, T w)
+ : x(x), y(y), z(z), w(w) { }
+
+ Vector4Base(const T xyzw[4])
+ : x(xyzw[0]), y(xyzw[1]), z(xyzw[2]), w(xyzw[3]) { }
+
+ Vector4Base(const Vector4Base<T>& other) = default;
+
+ inline T& operator[](size_t index) { return data[index]; }
+ inline const T& operator[](size_t index) const { return data[index]; }
+
+ bool operator==(const Vector4Base<T>& other) const {
+ for (uint32_t i = 0; i < 4; i++) {
+ if (data[i] != other.data[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ bool operator!=(const Vector4Base<T>& other) const {
+ return !operator==(other);
+ }
+
+ Vector4Base operator-() const { return {-x, -y, -z, -w}; }
+
+ Vector4Base operator+(const Vector4Base<T>& other) const {
+ return {x + other.x, y + other.y, z + other.z, w + other.w};
+ }
+
+ Vector4Base operator-(const Vector4Base<T>& other) const {
+ return {x - other.x, y - other.y, z - other.z, w - other.w};
+ }
+
+ Vector4Base operator*(T scalar) const {
+ return {scalar * x, scalar * y, scalar * z, scalar * w};
+ }
+
+ Vector4Base operator*(const Vector4Base<T>& other) const {
+ Vector4Base result;
+ for (uint32_t i = 0; i < 4; i++)
+ result[i] = data[i] * other.data[i];
+ return result;
+ }
+
+ Vector4Base operator/(const Vector4Base<T>& other) const {
+ Vector4Base result;
+ for (uint32_t i = 0; i < 4; i++)
+ result[i] = data[i] / other.data[i];
+ return result;
+ }
+
+ Vector4Base operator/(T scalar) const {
+ return {x / scalar, y / scalar, z / scalar, w / scalar};
+ }
+
+ Vector4Base& operator+=(const Vector4Base<T>& other) {
+ x += other.x;
+ y += other.y;
+ z += other.z;
+ w += other.w;
+
+ return *this;
+ }
+
+ Vector4Base& operator-=(const Vector4Base<T>& other) {
+ x -= other.x;
+ y -= other.y;
+ z -= other.z;
+ w -= other.w;
+
+ return *this;
+ }
+
+ Vector4Base& operator*=(T scalar) {
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ w *= scalar;
+
+ return *this;
+ }
+
+ Vector4Base& operator/=(T scalar) {
+ x /= scalar;
+ y /= scalar;
+ z /= scalar;
+ w /= scalar;
+
+ return *this;
+ }
+
+ union {
+ T data[4];
+ struct {
+ T x, y, z, w;
+ };
+ struct {
+ T r, g, b, a;
+ };
+ };
+
+ };
+
+ template <typename T>
+ inline Vector4Base<T> operator*(T scalar, const Vector4Base<T>& vector) {
+ return vector * scalar;
+ }
+
+ template <typename T>
+ float dot(const Vector4Base<T>& a, const Vector4Base<T>& b) {
+ return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
+ }
+
+ template <typename T>
+ T lengthSqr(const Vector4Base<T>& a) { return dot(a, a); }
+
+ template <typename T>
+ float length(const Vector4Base<T>& a) { return std::sqrt(float(lengthSqr(a))); }
+
+ template <typename T>
+ Vector4Base<T> normalize(const Vector4Base<T>& a) { return a * T(1.0f / length(a)); }
+
+ template <typename T>
+ std::ostream& operator<<(std::ostream& os, const Vector4Base<T>& v) {
+ return os << "Vector4(" << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << ")";
+ }
+
+ using Vector4 = Vector4Base<float>;
+ using Vector4i = Vector4Base<int>;
+
+ static_assert(sizeof(Vector4) == sizeof(float) * 4);
+ static_assert(sizeof(Vector4i) == sizeof(int) * 4);
+
+ inline Vector4 replaceNaN(Vector4 a) {
+ Vector4 result;
+ __m128 value = _mm_load_ps(a.data);
+ __m128 mask = _mm_cmpeq_ps(value, value);
+ value = _mm_and_ps(value, mask);
+ _mm_store_ps(result.data, value);
+ return result;
+ }
+
+}