From 53f0604556d1136ebfb3cffda68364f68de99703 Mon Sep 17 00:00:00 2001 From: Quaylyn Rimer Date: Sat, 19 Jul 2025 02:53:12 -0600 Subject: [PATCH] feat: Add IO and process priority controls This commit introduces new functionality to manage IO and CPU priority for games running through Proton, addressing performance issues during heavy IO activity (GitHub Issue #8910). - Added to enable or disable the feature. - Added to set the IO scheduling priority. - Added to set the CPU scheduling priority. - Implemented the core logic in the script to apply these settings using none: prio 0 and 0. - Updated and with documentation for the new environment variables and their usage. --- README.md | 3 ++ docs/THREAD_PRIORITY.md | 62 +++++++++++++++++++++++++++++++++++--- proton | 67 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cdb422f5a4..960feee647 100644 --- a/README.md +++ b/README.md @@ -309,6 +309,9 @@ the Wine prefix. Removing the option will revert to the previous behavior. | `noopwr` | `WINE_DISABLE_VULKAN_OPWR` | Enable hack to disable Vulkan other process window rendering which sometimes causes issues on Wayland due to blit being one frame behind. | | `hidenvgpu` | `PROTON_HIDE_NVIDIA_GPU` | Force Nvidia GPUs to always be reported as AMD GPUs. Some games require this if they depend on Windows-only Nvidia driver functionality. See also DXVK's nvapiHack config, which only affects reporting from Direct3D. | | | `WINE_FULLSCREEN_INTEGER_SCALING` | Enable integer scaling mode, to give sharp pixels when upscaling. | +| | `PROTON_ENABLE_IO_PRIORITY` | Enable IO and CPU priority optimizations to improve game responsiveness during heavy IO activity like recording. Set to `0` to disable. Default: `1` (enabled). | +| | `PROTON_IO_PRIORITY` | Set IO scheduling priority for games (0-7, lower = higher priority). Default: `2` (high priority). Only used when `PROTON_ENABLE_IO_PRIORITY=1`. | +| | `PROTON_NICE_VALUE` | Set CPU scheduling priority (nice value) for games (-20 to 19, lower = higher priority). Default: `-2` (slightly higher priority). Requires proper system configuration. | | `cmdlineappend:` | | Append the string after the colon as an argument to the game command. May be specified more than once. Escape commas and backslashes with a backslash. | | `xalia` | `PROTON_USE_XALIA` | Enable Xalia, a program that can add a gamepad UI for some keyboard/mouse interfaces. | | `seccomp` | `PROTON_USE_SECCOMP` | **Note: Obsoleted in Proton 5.13.** In older versions, enable seccomp-bpf filter to emulate native syscalls, required for some DRM protections to work. | diff --git a/docs/THREAD_PRIORITY.md b/docs/THREAD_PRIORITY.md index 1f340651d0..f8c619eec1 100644 --- a/docs/THREAD_PRIORITY.md +++ b/docs/THREAD_PRIORITY.md @@ -1,10 +1,14 @@ -# Thread priorities +# Thread priorities and IO scheduling Proton supports fine-grained thread priority control using `setpriority(2)` to set thread niceness values corresponding to the game threads' Windows base -priority levels. However, most default Linux configurations don't allow -individual threads to raise their priority, so some system configuration is -likely required. +priority levels. Additionally, Proton includes IO priority optimizations to +improve game responsiveness during heavy IO activity. + +## Thread Priority Configuration + +Most default Linux configurations don't allow individual threads to raise their +priority, so some system configuration is likely required. It can be configured as a privileged user by editing the `/etc/security/limits.conf` file, or using the `/etc/security/limits.d/` conf @@ -18,3 +22,53 @@ Where -15 could be any value between [-20,0] that will correspond to the minimum niceness (the highest priority) a thread can get to. The lower the value, the more CPU time a high priority thread will get, at the expense of others and other processes, possibly making the system less responsive. + +## IO Priority Optimization + +Proton automatically configures IO scheduling and CPU priorities to improve game +responsiveness during heavy IO operations (such as screen recording, file +downloads, or system backups). This feature is enabled by default and can help +prevent games from becoming unresponsive during intensive disk activity. + +### How it works + +- **IO Priority**: Sets games to use the "best-effort" IO scheduling class with + high priority (priority 2 by default), ensuring games get preferential access + to disk resources during heavy IO operations. + +- **CPU Priority**: Sets a slightly higher CPU scheduling priority (nice value -2) + to give games a small advantage in CPU scheduling during IO-intensive periods. + +### Environment Variables + +- `PROTON_ENABLE_IO_PRIORITY`: Enable/disable IO priority optimizations. + Set to `0` to disable, `1` to enable (default). + +- `PROTON_IO_PRIORITY`: IO scheduling priority (0-7, lower = higher priority). + Default is `2` (high priority). Only used when IO priority is enabled. + +- `PROTON_NICE_VALUE`: CPU scheduling priority (-20 to 19, lower = higher priority). + Default is `-2` (slightly higher priority). Requires proper limits.conf configuration. + +### Usage Examples + +```bash +# Disable IO priority optimization entirely +PROTON_ENABLE_IO_PRIORITY=0 %command% + +# Use maximum IO priority (may affect system responsiveness) +PROTON_IO_PRIORITY=0 %command% + +# Use normal CPU priority but keep IO priority optimization +PROTON_NICE_VALUE=0 %command% + +# Conservative settings for shared systems +PROTON_IO_PRIORITY=4 PROTON_NICE_VALUE=0 %command% +``` + +### Troubleshooting + +If you experience system unresponsiveness while gaming during heavy IO operations, +you can adjust or disable these optimizations. Conservative values or disabling +the feature entirely may be necessary on systems with limited resources or +specific workload requirements. diff --git a/proton b/proton index c0e0c43687..6b6ed741f1 100755 --- a/proton +++ b/proton @@ -1786,7 +1786,72 @@ class Session: def run_proc(self, args, local_env=None): if local_env is None: local_env = self.env - return subprocess.call(args, env=local_env, stderr=self.log_file, stdout=self.log_file) + + # Check if IO priority optimization is enabled (default: enabled) + enable_io_priority = os.environ.get("PROTON_ENABLE_IO_PRIORITY", "1") == "1" + + # Set up IO and CPU scheduling for better performance during heavy IO activity + def preexec_fn(): + if not enable_io_priority: + return + + try: + # Set IO scheduling class to best-effort with high priority (lower value = higher priority) + # This helps games maintain responsiveness during heavy IO operations like recording + # ioprio_set(IOPRIO_WHO_PROCESS, pid, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, priority)) + # IOPRIO_CLASS_BE = 1, priority 2 (range 0-7, 0=highest) + import ctypes + libc = ctypes.CDLL("libc.so.6", use_errno=True) + + # Constants for ioprio_set + IOPRIO_WHO_PROCESS = 1 + IOPRIO_CLASS_BE = 1 # Best-effort scheduling class + + def IOPRIO_PRIO_VALUE(ioprio_class, ioprio_data): + return (ioprio_class << 13) | ioprio_data + + # Allow user to customize IO priority (default: 2 = high priority) + io_priority = int(os.environ.get("PROTON_IO_PRIORITY", "2")) + if io_priority < 0 or io_priority > 7: + io_priority = 2 # Fallback to safe default + + # Set IO priority to best-effort class with specified priority + ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, io_priority) + result = libc.ioprio_set(IOPRIO_WHO_PROCESS, 0, ioprio) + if result != 0: + # If ioprio_set fails, it's not critical - log and continue + errno = ctypes.get_errno() + log(f"Warning: Could not set IO priority (errno: {errno}). Continuing anyway.") + + # Set CPU scheduler priority (nice value) to slightly higher than normal + # This gives games a small CPU scheduling advantage during heavy IO + try: + # Allow user to customize nice value (default: -2 = slightly higher priority) + nice_value = int(os.environ.get("PROTON_NICE_VALUE", "-2")) + if nice_value < -20 or nice_value > 19: + nice_value = -2 # Fallback to safe default + + os.setpriority(os.PRIO_PROCESS, 0, nice_value) + except OSError as e: + # If we can't set negative nice (higher priority), try positive nice + if e.errno == 1: # EPERM - Operation not permitted + try: + # Set nice value to 0 (normal priority) as fallback + os.setpriority(os.PRIO_PROCESS, 0, 0) + except OSError: + # If even that fails, continue without changing priority + log("Warning: Could not adjust process priority. Consider configuring /etc/security/limits.conf for better performance.") + else: + log(f"Warning: Could not set process priority: {e}") + + except Exception as e: + # Don't fail the whole process if priority setting fails + log(f"Warning: Could not configure process scheduling: {e}") + + if enable_io_priority: + return subprocess.call(args, env=local_env, stderr=self.log_file, stdout=self.log_file, preexec_fn=preexec_fn) + else: + return subprocess.call(args, env=local_env, stderr=self.log_file, stdout=self.log_file) def run(self): if shutil.which('steam-runtime-launcher-interface-0') is not None: