summary refs log tree commit diff
path: root/pkgs/misc/dxvk
diff options
context:
space:
mode:
authorRandy Eckenrode <randy@largeandhighquality.com>2022-03-09 11:18:05 -0500
committerRandy Eckenrode <randy@largeandhighquality.com>2022-03-09 11:18:05 -0500
commite2369cb9c90b92a465aeb40d62f2abd467c91543 (patch)
treee2d5093c583925a9e56bc35d3af3b0478551bbcb /pkgs/misc/dxvk
parent31c068329c532ff91929f6d7cc4262cc2463dd70 (diff)
downloadnixpkgs-e2369cb9c90b92a465aeb40d62f2abd467c91543.tar
nixpkgs-e2369cb9c90b92a465aeb40d62f2abd467c91543.tar.gz
nixpkgs-e2369cb9c90b92a465aeb40d62f2abd467c91543.tar.bz2
nixpkgs-e2369cb9c90b92a465aeb40d62f2abd467c91543.tar.lz
nixpkgs-e2369cb9c90b92a465aeb40d62f2abd467c91543.tar.xz
nixpkgs-e2369cb9c90b92a465aeb40d62f2abd467c91543.tar.zst
nixpkgs-e2369cb9c90b92a465aeb40d62f2abd467c91543.zip
dxvk: refactor to better support Darwin and Linux
- 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.
Diffstat (limited to 'pkgs/misc/dxvk')
-rw-r--r--pkgs/misc/dxvk/darwin-thread-primitives.patch186
-rw-r--r--pkgs/misc/dxvk/default.nix35
-rw-r--r--pkgs/misc/dxvk/dxvk.nix12
3 files changed, 213 insertions, 20 deletions
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<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;
+-
+-  };
++  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";