From e2369cb9c90b92a465aeb40d62f2abd467c91543 Mon Sep 17 00:00:00 2001 From: Randy Eckenrode Date: Wed, 9 Mar 2022 11:18:05 -0500 Subject: dxvk: refactor to better support Darwin and Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move the synchronization primitive changes to their own patch, so it can be applied conditionally on Darwin. Also, document why this change is needed; and - Refactor how `src` is handled to support allowing Darwin and Linux to diverge in case the Darwin patches do not apply to the latest version. This should address some post-merger concerns that were raised about Darwin’s blocking updates for Linux. --- pkgs/misc/dxvk/darwin-thread-primitives.patch | 186 ++++++++++++++++++++++++++ pkgs/misc/dxvk/default.nix | 35 +++-- pkgs/misc/dxvk/dxvk.nix | 12 -- 3 files changed, 213 insertions(+), 20 deletions(-) create mode 100644 pkgs/misc/dxvk/darwin-thread-primitives.patch (limited to 'pkgs/misc/dxvk') diff --git a/pkgs/misc/dxvk/darwin-thread-primitives.patch b/pkgs/misc/dxvk/darwin-thread-primitives.patch new file mode 100644 index 00000000000..c008099407c --- /dev/null +++ b/pkgs/misc/dxvk/darwin-thread-primitives.patch @@ -0,0 +1,186 @@ +diff --git a/src/util/thread.h b/src/util/thread.h +index 28aeca8a..db5c9913 100644 +--- a/src/util/thread.h ++++ b/src/util/thread.h +@@ -149,178 +149,8 @@ namespace dxvk { + } + } + +- +- /** +- * \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& lock) { +- auto srw = lock.mutex()->native_handle(); +- SleepConditionVariableSRW(&m_cond, srw, INFINITE, 0); +- } +- +- template +- void wait(std::unique_lock& lock, Predicate pred) { +- while (!pred()) +- wait(lock); +- } +- +- template +- std::cv_status wait_until(std::unique_lock& lock, const std::chrono::time_point& time) { +- auto now = Clock::now(); +- +- return (now < time) +- ? wait_for(lock, now - time) +- : std::cv_status::timeout; +- } +- +- template +- bool wait_until(std::unique_lock& lock, const std::chrono::time_point& time, Predicate pred) { +- if (pred()) +- return true; +- +- auto now = Clock::now(); +- return now < time && wait_for(lock, now - time, pred); +- } +- +- template +- std::cv_status wait_for(std::unique_lock& lock, const std::chrono::duration& timeout) { +- auto ms = std::chrono::duration_cast(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 +- bool wait_for(std::unique_lock& lock, const std::chrono::duration& 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; +- +- }; ++ using mutex = std::mutex; ++ using recursive_mutex = std::recursive_mutex; ++ using condition_variable = std::condition_variable; + + } diff --git a/pkgs/misc/dxvk/default.nix b/pkgs/misc/dxvk/default.nix index 61e2d78b45c..d1ea829798a 100644 --- a/pkgs/misc/dxvk/default.nix +++ b/pkgs/misc/dxvk/default.nix @@ -1,33 +1,52 @@ { lib , pkgs -, stdenv +, hostPlatform +, stdenvNoCC , fetchFromGitHub , pkgsCross }: let + inherit (hostPlatform.uname) system; + # DXVK needs to be a separate derivation because it’s actually a set of DLLs for Windows that # needs to be built with a cross-compiler. dxvk32 = pkgsCross.mingw32.callPackage ./dxvk.nix { inherit (self) src version dxvkPatches; }; dxvk64 = pkgsCross.mingwW64.callPackage ./dxvk.nix { inherit (self) src version dxvkPatches; }; + # Split out by platform to make maintenance easy in case supported versions on Darwin and other + # platforms diverge (due to the need for Darwin-specific patches that would fail to apply). + # Should that happen, set `darwin` to the last working `rev` and `hash`. + srcs = rec { + darwin = { inherit (default) rev hash version; }; + default = { + rev = "v${self.version}"; + hash = "sha256-/zH6vER/6s/d+Tt181UJOa97sqdkJyKGw6E36+1owzQ="; + version = "1.10"; + }; + }; + # Use the self pattern to support overriding `src` and `version` via `overrideAttrs`. A recursive # attrset wouldn’t work. - self = stdenv.mkDerivation { + self = stdenvNoCC.mkDerivation { name = "dxvk"; - version = "1.10"; + inherit (srcs."${system}" or srcs.default) version; src = fetchFromGitHub { owner = "doitsujin"; repo = "dxvk"; - rev = "v${self.version}"; - hash = "sha256-/zH6vER/6s/d+Tt181UJOa97sqdkJyKGw6E36+1owzQ="; + inherit (srcs."${system}" or srcs.default) rev hash; }; - # Patch DXVK to work with MoltenVK even though it doesn’t support some required features. - # Some games will work poorly (particularly Unreal Engine 4 games), but others work pretty well. # Override this to patch DXVK itself (rather than the setup script). - dxvkPatches = lib.optional stdenv.isDarwin ./darwin-dxvk-compat.patch; + dxvkPatches = lib.optionals stdenvNoCC.isDarwin [ + # Patch DXVK to work with MoltenVK even though it doesn’t support some required features. + # Some games work poorly (particularly Unreal Engine 4 games), but others work pretty well. + ./darwin-dxvk-compat.patch + # Use synchronization primitives from the C++ standard library to avoid deadlocks on Darwin. + # See: https://www.reddit.com/r/macgaming/comments/t8liua/comment/hzsuce9/ + ./darwin-thread-primitives.patch + ]; outputs = [ "out" "bin" "lib" ]; diff --git a/pkgs/misc/dxvk/dxvk.nix b/pkgs/misc/dxvk/dxvk.nix index 40b674df922..2eb4f30df2b 100644 --- a/pkgs/misc/dxvk/dxvk.nix +++ b/pkgs/misc/dxvk/dxvk.nix @@ -19,18 +19,6 @@ stdenv.mkDerivation { patches = dxvkPatches; - # Replace use of DXVK’s threading classes with the ones from the C++ standard library, which uses - # mcfgthreads in nixpkgs. - postPatch = '' - for class in mutex recursive_mutex condition_variable; do - for file in $(grep -rl dxvk::$class *); do - if [ "$(basename "$file")" != "thread.h" ]; then - substituteInPlace "$file" --replace dxvk::$class std::$class - fi - done - done - ''; - mesonFlags = let arch = if stdenv.is32bit then "32" else "64"; -- cgit 1.4.1