diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/libs/dxvk-native-1.9.2a/src/util | |
parent | Initial commit. (diff) | |
download | virtualbox-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')
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; + } + +} |