diff --git a/AGENTS.md b/AGENTS.md index 63d2afd..63aa7f1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -231,6 +231,12 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine `linux-cachyos` is clang-built, `-bore` GCC-built: let DKMS pick the compiler, never pass `LLVM=1`. Test shutdown on the real machine for every new major kernel. +- VRAM booster (`vram`, `lib/vram-booster.sh`, a default sub-option of + `machine`, 2.3.0): CachyOS's `dmemcg-booster` (system + user service) and + `plasma-foreground-booster`, like SteamOS 3.9's VRAM management. The + latter only starts with `kcgroupsrc [Foreground Booster] autostart=true` + (set with `kset`). Disable removes only the packages it installed + (`state_get vram installed_pkgs`). - `Relogin=true` means a gamescope that fails to start is relaunched in a tight loop; keep that in mind when changing session handling. diff --git a/CHANGELOG.md b/CHANGELOG.md index f92b5fd..adbbaf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,13 @@ All notable changes, per version and per commit. Versions follow `steamify.sh`. Versions before 0.7.0 were numbered afterwards, one per merged pull request. +## 2.3.0 - 2026-09-27 + +- **feat: VRAM booster for the Steam Machine** + ## 2.2.0 - 2026-09-27 -- **docs: Changelog entries per commit for 2.2.0** +- `ef585c0` **docs: Changelog entries per commit for 2.2.0** - `32795bd` **feat(ui): Update badge on the progress screen** - `3c8ca65` **fix(ui): Never write the sudo password to a file; Update label on the progress screen** - The app gave `sudo` the password through a short-lived file; the helper diff --git a/README.md b/README.md index eb08c43..be8b5ff 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,9 @@ What Steamify can do on a Valve Steam Machine: - **Power-off fix** - on by default: shutting down really turns the Steam Machine off, also with the newest CachyOS kernel ([details](TECHNICAL.md#power-off-fix-steam-machine)). + - **VRAM booster** - on by default: the game you're playing keeps its + video memory and background apps make room, like SteamOS 3.9 + ([details](TECHNICAL.md#vram-booster-steam-machine)). - **Update BIOS** - installs Valve's newest Steam Machine BIOS. Never ticked by default, at your own risk, and only after two warnings ([details](TECHNICAL.md#bios-updates-steam-machine)). diff --git a/TECHNICAL.md b/TECHNICAL.md index 2bb7b10..cd75c37 100644 --- a/TECHNICAL.md +++ b/TECHNICAL.md @@ -236,6 +236,29 @@ Where Steam Machine support was set up before 2.2.0, the menu ticks the power-off fix as a new default sub-option (see feature versions below), so a normal run adds it. +## VRAM booster (Steam Machine) + +SteamOS 3.9 manages the dGPU's VRAM per cgroup: the game in front is +protected, background apps are evicted first. Without it a game that needs +most of the Steam Machine's 8 GB can be pushed into system RAM by the +desktop and other apps, and stutter. The kernel side is the `dmem` cgroup +controller (7.2); CachyOS packages the userspace side, which the **VRAM +booster** sub-option of Steam Machine support installs (ticked along with +it, can be unticked; new in 2.3.0, so existing setups get it ticked): + +- `dmemcg-booster`: `dmemcg-booster-system.service` enables the `dmem` + controller and sets `dmem.low` for the cgroup in front; + `dmemcg-booster-user.service` runs in the user session. +- `plasma-foreground-booster`: tells it which window is in front on the + desktop. It only starts with `autostart=true` under `[Foreground Booster]` + in `kcgroupsrc`, which the wizard sets (and reverts). + +Check it with `cat /sys/fs/cgroup/cgroup.subtree_control` (lists `dmem`) and +the `dmem.low` of the slices under `user@.service`. Turning it off +disables the services and removes the packages the wizard installed. +Not added: a fixed `dmem.max` cap for `app.slice` (as some guides do); only +worth it if the booster itself turns out to cause stutter. + ## Feature versions (updates) Each component has a feature version (`FEATURE_VERSION` in `lib/menu.sh`): @@ -327,6 +350,7 @@ immediately, which can turn into a loop - see | `lib/cec.sh` | HDMI-CEC: Valve's `cecd` and friends from its `holo` repository | | `lib/steam-machine.sh` | Steam Machine support: LED driver, LED access, steamos-manager | | `lib/fremont-poweroff.sh` | Steam Machine support: the power-off fix (DKMS module from `patches/`) | +| `lib/vram-booster.sh` | Steam Machine support: the VRAM booster (`dmemcg-booster`, `plasma-foreground-booster`) | | `patches/` | Module sources and patches the scripts build or apply (`patch_file`); see its README | | `.github/tools/bundle.sh` | Builds the single-file version (`dist/steamify.sh`), with `patches/` embedded | | `.github/workflows/bundle.yml` | Builds and checks it on every push; publishes it on `main` | diff --git a/lib/backend.sh b/lib/backend.sh index da6e867..fdd0241 100644 --- a/lib/backend.sh +++ b/lib/backend.sh @@ -162,7 +162,8 @@ backend_apply() { [[ "${WANTED[single]:-0}" == 1 ]] && WANTED[gaming]=1 [[ "${WANTED[gaming]:-0}" == 0 ]] && { WANTED[single]=0; WANTED[boot]=0; } [[ "${WANTED[poweroff]:-0}" == 1 ]] && WANTED[machine]=1 - [[ "${WANTED[machine]:-0}" == 0 ]] && WANTED[poweroff]=0 + [[ "${WANTED[vram]:-0}" == 1 ]] && WANTED[machine]=1 + [[ "${WANTED[machine]:-0}" == 0 ]] && { WANTED[poweroff]=0; WANTED[vram]=0; } [[ "${WANTED[kpin]:-0}" == 1 ]] && WANTED[machine]=1 [[ "${WANTED[machine]:-0}" == 0 ]] && WANTED[kpin]=0 [[ "${WANTED[hdmi]:-0}" == 1 ]] && { WANTED[machine]=1; WANTED[kpin]=1; } diff --git a/lib/menu.sh b/lib/menu.sh index 2d8baec..1202536 100644 --- a/lib/menu.sh +++ b/lib/menu.sh @@ -5,12 +5,12 @@ # Menu order. Components are turned on in this order and off in reverse; # gaming must come first (single user builds on it). -COMPONENTS=(gaming boot theme glyphs single launcher cec machine poweroff kpin hdmi bios) +COMPONENTS=(gaming boot theme glyphs single launcher cec machine poweroff vram kpin hdmi bios) # One-off actions rather than on/off components: never preselected, never # re-applied, not listed as on or off. ACTIONS=(bios) # Sub-options, shown indented under their parent and only while it's ticked. -declare -A PARENT=([boot]=gaming [poweroff]=machine [kpin]=machine [hdmi]=machine [bios]=machine) +declare -A PARENT=([boot]=gaming [poweroff]=machine [vram]=machine [kpin]=machine [hdmi]=machine [bios]=machine) # Never preselected on a first run: booting into the desktop is a choice, # gamescope is the default; HDMI-CEC is opt-in (it can wake the machine or # upset other devices on the TV, even on SteamOS), except on a Steam Machine, @@ -26,7 +26,7 @@ NO_PRESELECT=(boot cec kpin hdmi) FEATURE_BASELINE=2.1.0 declare -A FEATURE_VERSION=( [gaming]=2.1.0 [boot]=2.1.0 [theme]=2.1.0 [glyphs]=2.1.0 [single]=2.1.0 - [launcher]=2.1.0 [cec]=2.1.0 [machine]=2.2.0 [poweroff]=2.2.0 + [launcher]=2.1.0 [cec]=2.1.0 [machine]=2.2.0 [poweroff]=2.2.0 [vram]=2.3.0 [kpin]=2.1.0 [hdmi]=2.1.0 ) @@ -40,6 +40,7 @@ declare -A LABEL=( [cec]="HDMI-CEC: use Steam with the TV remote, TV on/off with the PC (experimental)" [machine]="Steam Machine support: LED bar driver, hardware settings in Steam" [poweroff]="Power-off fix: the Steam Machine stays off after shutting down" + [vram]="VRAM booster: the game in front keeps its VRAM, background apps make room" [kpin]="Pin the kernel to $PINNED_KERNEL_VER (untick for CachyOS's current kernel)" [hdmi]="HDMI refresh boost: highest refresh your HDMI display runs" [bios]="Update BIOS" @@ -48,7 +49,7 @@ declare -A CURRENT WANTED component_available() { case "$1" in - machine|poweroff) machine_available ;; + machine|poweroff|vram) machine_available ;; kpin) kpin_available ;; hdmi) hdmi_available ;; bios) bios_available ;; @@ -157,9 +158,11 @@ toggle_component() { if [[ "$c" == gaming && "${WANTED[gaming]}" == 0 ]]; then WANTED[single]=0; WANTED[boot]=0; fi # Where to boot to is part of the conversion, too. if [[ "$c" == boot && "${WANTED[boot]}" == 1 ]]; then WANTED[gaming]=1; fi - # The power-off fix is opt-out: ticked along with Steam Machine support. - if [[ "$c" == machine ]]; then WANTED[poweroff]=${WANTED[machine]}; fi + # The power-off fix and VRAM booster are opt-out: ticked along with + # Steam Machine support. + if [[ "$c" == machine ]]; then WANTED[poweroff]=${WANTED[machine]}; WANTED[vram]=${WANTED[machine]}; fi if [[ "$c" == poweroff && "${WANTED[poweroff]}" == 1 ]]; then WANTED[machine]=1; fi + if [[ "$c" == vram && "${WANTED[vram]}" == 1 ]]; then WANTED[machine]=1; fi if [[ "$c" == machine && "${WANTED[machine]}" == 0 ]]; then WANTED[kpin]=0; fi if [[ "$c" == kpin && "${WANTED[kpin]}" == 1 ]]; then WANTED[machine]=1; fi # HDMI refresh boost and the BIOS update sit under Steam Machine support; diff --git a/lib/vram-booster.sh b/lib/vram-booster.sh new file mode 100644 index 0000000..4815df1 --- /dev/null +++ b/lib/vram-booster.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# "VRAM booster" menu item (vram), a sub-option of Steam Machine support +# ticked along with it (opt-out), like SteamOS 3.9's dGPU VRAM management: +# the game in front keeps its VRAM, background apps are evicted first, so a +# game on the 8 GB GPU doesn't spill into system RAM and stutter. The kernel +# side is the dmem cgroup controller (7.2); CachyOS packages the userspace +# side: dmemcg-booster (a system service that enables the controller and +# sets the limits, plus a user service) and plasma-foreground-booster, which +# tells it which window is in front on the desktop. The latter only starts +# with "autostart" set in kcgroupsrc. +# Sourced by steamify.sh; not meant to be run on its own. + +VRAM_PKGS=(dmemcg-booster plasma-foreground-booster) + +vram_status() { + pacman -Q "${VRAM_PKGS[@]}" >/dev/null 2>&1 && + systemctl is-enabled -q dmemcg-booster-system.service 2>/dev/null && + systemctl --user is-enabled -q dmemcg-booster-user.service 2>/dev/null +} + +vram_enable() { + local p missing=() + info "Installing the VRAM booster (the game in front keeps its VRAM)..." + for p in "${VRAM_PKGS[@]}"; do pacman -Q "$p" >/dev/null 2>&1 || missing+=("$p"); done + if ((${#missing[@]})); then + sudo pacman -S --needed --noconfirm "${missing[@]}" || + { err "Installing ${missing[*]} failed."; return 1; } + state_set vram installed_pkgs "${missing[*]}" + fi + sudo systemctl enable --now dmemcg-booster-system.service >/dev/null 2>&1 || + { err "Starting dmemcg-booster failed."; return 1; } + systemctl --user enable --now dmemcg-booster-user.service >/dev/null 2>&1 || + { err "Starting dmemcg-booster for $USER failed."; return 1; } + kset vram kcgroupsrc "Foreground Booster" autostart true + # Started by the Plasma session from now on; start it in this one too. + [[ "${XDG_CURRENT_DESKTOP:-}" == KDE ]] && + systemctl --user start plasma-foreground-booster.service >/dev/null 2>&1 + ok "VRAM booster on: the game in front keeps its VRAM." +} + +vram_disable() { + local pkgs + systemctl --user disable --now plasma-foreground-booster.service dmemcg-booster-user.service >/dev/null 2>&1 + sudo systemctl disable --now dmemcg-booster-system.service >/dev/null 2>&1 + krevert vram + pkgs="$(state_get vram installed_pkgs)" + # shellcheck disable=SC2086 # a list of package names + [[ -n "$pkgs" ]] && sudo pacman -Rns --noconfirm $pkgs >/dev/null 2>&1 + state_clear vram + ok "VRAM booster off." +} diff --git a/steamify.sh b/steamify.sh index ea40854..971b826 100755 --- a/steamify.sh +++ b/steamify.sh @@ -15,11 +15,11 @@ set -uo pipefail # Release version, see CHANGELOG.md. -VERSION=2.2.0 +VERSION=2.3.0 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff hdmi-refresh cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut menu backend; do +for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut menu backend; do # shellcheck source=/dev/null source "$SCRIPT_DIR/lib/$lib.sh" done diff --git a/ui/qml/Main.qml b/ui/qml/Main.qml index 9f2f700..4f3d0dd 100644 --- a/ui/qml/Main.qml +++ b/ui/qml/Main.qml @@ -72,6 +72,9 @@ ApplicationWindow { poweroff: { label: "Power-off fix", hint: "Stays off after shutting down", body: "With recent kernels the Steam Machine starts again right after shutting down: the firmware leaves a wake bit set, and newer kernels (7.2, and updates of 6.x, 7.0 and 7.1) no longer clear it. Valve's own kernel clears it; this small module does the same right before power-off.", changes: ["steamify-fremont-poweroff module for each installed kernel (DKMS)", "Only on a Steam Machine, only touches that one wake bit", "Off: recent kernels may start it again after shutting down"] }, + vram: { label: "VRAM booster", hint: "The game in front keeps its VRAM", + body: "Like SteamOS 3.9: the game you're playing keeps its video memory, and background apps are moved out first. Without it a game that needs most of the 8 GB can spill into system RAM and stutter.", + changes: ["dmemcg-booster (system and user service)", "plasma-foreground-booster, which tells it which window is in front", "Off: removed again, unless you had installed them yourself"] }, kpin: { label: "Pin the kernel", hint: "Untick for CachyOS's current kernel", body: "Keeps the Steam Machine on CachyOS kernel 7.1.6. Untick it to go back to CachyOS's current kernel; HDMI refresh boost goes with it.", changes: ["linux-cachyos from Steamify's release (signature checked)", "Kept in /var/cache/steamify/kernel", "Added to IgnorePkg"] }, @@ -250,8 +253,9 @@ ApplicationWindow { if (id === "hdmi" && !hdmiChoice && hdmiSaved.length) { openHdmiList(); return; } if (id === "hdmi" && w.hdmi && !nowOn("hdmi") && !hdmiChoice) { startHdmi(); return; } if (id === "hdmi" && !w.hdmi) hdmiChoice = ""; - if (id === "machine") w.poweroff = w.machine; + if (id === "machine") { w.poweroff = w.machine; w.vram = w.machine; } if (id === "poweroff" && w.poweroff) w.machine = true; + if (id === "vram" && w.vram) w.machine = true; if (id === "machine" && !w.machine) w.kpin = false; if (id === "kpin" && w.kpin) w.machine = true; if (id === "hdmi" && w.hdmi) { w.machine = true; w.kpin = true; }