Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down
62 changes: 58 additions & 4 deletions docs/THREAD_PRIORITY.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.
67 changes: 66 additions & 1 deletion proton
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down