steam: auto-egpu-detection via VDF-patch statt Shell-Wrapper

Proton sandboxed keine Env-Vars aus dem Wrapper → direkt in LaunchOptions schreiben.
steam-egpu: launcher checkt card0 vor Steam-Start und patcht localconfig.vdf
steam-egpu-toggle.py: schaltet 120+ Spiele auf einmal um
udev: automatisch bei eGPU-Connect/Disconnect
This commit is contained in:
Kay Türtscher 2026-06-06 19:43:46 +02:00
parent 44e6b00fb7
commit ed130e79bd
3 changed files with 132 additions and 19 deletions

View file

@ -1,18 +1,32 @@
#!/bin/bash
# steam-egpu — Wrapper der vor jedem Steam-Start die Launch Options sync't
# Ersetzt den normalen "steam"-Aufruf. Trägt superx-game-wrapper
# automatisch in alle Spiele ein, die noch keine LaunchOptions haben.
# steam-egpu — Auto-eGPU-Detection vor jedem Steam-Start
#
# Installation: siehe tools/install-egpu.sh (Schritt 5)
# Erkennt ob die eGPU da ist und patcht die LaunchOptions entsprechend.
# Kein Shell-Wrapper für einzelne Spiele (Proton sandboxed Env-Vars),
# sondern direktes Patchen der VDF-Datei.
#
# Installation: tools/install-egpu.sh oder manuell:
# cp tools/steam-egpu ~/.local/bin/steam-egpu
# Hyprland exec-once: ~/.local/bin/steam-egpu (startet Steam mit Sync)
#
# Als Steam-Launcher verwenden (statt /usr/bin/steam):
# cp configs/steam-egpu.desktop ~/.local/share/applications/
WRAPPER_STRING="/home/nepharius/SuperX-goes-Arch/tools/superx-game-wrapper.sh %command%"
CONFIG="$HOME/.local/share/Steam/userdata/19491507/config/localconfig.vdf"
SYNC_SCRIPT="/home/nepharius/SuperX-goes-Arch/.venv/bin/python3 /home/nepharius/SuperX-goes-Arch/tools/steam-egpu-patch-all.py"
VDF="$HOME/.local/share/Steam/userdata/19491507/config/localconfig.vdf"
TOGGLE="$HOME/SuperX-goes-Arch/.venv/bin/python3 $HOME/SuperX-goes-Arch/tools/steam-egpu-toggle.py"
EGPU_CARD="/dev/dri/card0"
# Sync: alle Spiele ohne LaunchOptions bekommen den Wrapper
if [ -f "$CONFIG" ]; then
$SYNC_SCRIPT -y 2>/dev/null &
# Prüfen ob eGPU da ist
if [ -e "$EGPU_CARD" ] && [ "$(cat /sys/class/drm/card0/device/vendor 2>/dev/null)" = "0x1002" ]; then
MODE="egpu"
echo "[steam-egpu] eGPU erkannt → setze DRI_PRIME=1" >&2
else
MODE="igpu"
echo "[steam-egpu] Keine eGPU → iGPU-Modus" >&2
fi
# Steam starten
# Nur patchen wenn nötig (Änderungen werden gezählt)
$TOGGLE "$MODE" 2>&1 | grep -q "Geändert: 0" && echo "[steam-egpu] Bereits aktuell, kein Patch nötig" >&2
# Steam weitergeben
exec /usr/bin/steam "$@"

View file

@ -0,0 +1,99 @@
#!/usr/bin/env python3
"""Schaltet Steam-Launch-Options zwischen eGPU und iGPU um.
Wird per udev getriggert wenn die eGPU verbunden/getrennt wird.
Patched localconfig.vdf direkt Steam muss danach neu gestartet werden.
Usage:
steam-egpu-toggle.py egpu # eGPU-Modus: DRI_PRIME=1 in alle Spiele
steam-egpu-toggle.py igpu # iGPU-Modus: DRI_PRIME entfernen
steam-egpu-toggle.py status # Aktuellen Modus anzeigen
"""
import sys, shutil
from pathlib import Path
import vdf
STEAM_USERDATA = Path.home() / ".local/share/Steam/userdata/19491507/config/localconfig.vdf"
EGPU_OPTS = "DRI_PRIME=1 VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/radeon_icd.json VKD3D_CONFIG=force_host_gpu DXVK_CONFIG=dxgi.allowUnsupportedDrivers=True %command%"
IGPU_OPTS = "%command%"
LAUNCH_KEY = "LaunchOptions"
# Spiele die NIEMALS angerührt werden (z.B. Proton, Steam Runtime, Tools)
SKIP_APPIDS = {
"1493710", # Proton Experimental
"4183110", # Steam Linux Runtime 4.0
"228980", # Steamworks Common Redistributables
}
def get_apps(config):
return config.get("UserLocalConfigStore", {}).get("Software", {}).get("Valve", {}).get("Steam", {}).get("apps", {})
def main():
if len(sys.argv) < 2:
print("Usage: steam-egpu-toggle.py {egpu|igpu|status}")
sys.exit(1)
cmd = sys.argv[1]
if not STEAM_USERDATA.exists():
print(f"ERROR: {STEAM_USERDATA} nicht gefunden")
sys.exit(1)
# Backup
backup = STEAM_USERDATA.with_suffix(".vdf.togglebak")
shutil.copy2(STEAM_USERDATA, backup)
with open(STEAM_USERDATA) as f:
config = vdf.load(f)
apps = get_apps(config)
if cmd == "status":
e_count = sum(1 for a in apps.values() if a.get(LAUNCH_KEY, "").startswith("DRI_PRIME=1"))
w_count = sum(1 for a in apps.values() if "superx-game-wrapper" in a.get(LAUNCH_KEY, ""))
o_count = sum(1 for a in apps.values() if a.get(LAUNCH_KEY, "") and not a[LAUNCH_KEY].startswith("DRI_PRIME=1") and "superx-game-wrapper" not in a[LAUNCH_KEY])
print(f"eGPU-Modus: {e_count} Spiele")
print(f"Wrapper: {w_count} Spiele")
print(f"Individuell:{o_count} Spiele")
return
target_opts = EGPU_OPTS if cmd == "egpu" else IGPU_OPTS
changed = 0
cleaned_wrapper = 0
for appid, appcfg in apps.items():
if appid in SKIP_APPIDS:
continue
current = appcfg.get(LAUNCH_KEY, "")
# Cleanup: wrapper ersetzen
if "superx-game-wrapper" in current:
appcfg[LAUNCH_KEY] = target_opts
cleaned_wrapper += 1
changed += 1
# Cleanup: leere oder fehlende LaunchOptions
elif not current or current == "%command%":
appcfg[LAUNCH_KEY] = target_opts
changed += 1
elif cmd == "egpu" and not current.startswith("DRI_PRIME=1"):
appcfg[LAUNCH_KEY] = target_opts
changed += 1
elif cmd == "igpu" and current.startswith("DRI_PRIME=1"):
appcfg[LAUNCH_KEY] = target_opts
changed += 1
with open(STEAM_USERDATA, "w") as f:
vdf.dump(config, f, pretty=True)
mode = "eGPU (DRI_PRIME=1)" if cmd == "egpu" else "iGPU (Standard)"
print(f"Modus: {mode}")
print(f"Geändert: {changed} Spiele (davon {cleaned_wrapper} Wrapper ersetzt)")
print(f"Backup: {backup}")
print(f"\n⚠️ Steam muss NEU GESTARTET werden!")
if __name__ == "__main__":
main()

View file

@ -3,15 +3,15 @@
# Installation: sudo cp udev/99-egpu-superx.rules /etc/udev/rules.d/
# sudo udevadm control --reload-rules && sudo udevadm trigger
#
# Triggert superx-egpu-perf.sh wenn die RX 9060 XT (1002:7590)
# über USB4/Thunderbolt verbunden oder getrennt wird.
# eGPU verbunden → schaltet Steam-Spiele auf DRI_PRIME=1
# eGPU getrennt → schaltet zurück auf iGPU
# RX 9060 XT (1002:7590) verbunden
SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7590", ACTION=="add", \
RUN+="/usr/local/bin/superx-egpu-perf.sh add"
RUN+="/usr/local/bin/superx-egpu-perf.sh add", \
RUN+="/usr/bin/su nepharius -c '/home/nepharius/SuperX-goes-Arch/.venv/bin/python3 /home/nepharius/SuperX-goes-Arch/tools/steam-egpu-toggle.py egpu'"
# RX 9060 XT getrennt
SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7590", ACTION=="remove", \
RUN+="/usr/local/bin/superx-egpu-perf.sh remove"
# JHL7440 Controller verbunden — triggert PCI-Rescan für dahinterliegende GPU
SUBSYSTEM=="pci", ATTR{vendor}=="0x8086", ATTR{device}=="0x15ef", ACTION=="add", \
RUN+="/usr/bin/bash -c 'echo 1 > /sys/bus/pci/rescan'"
RUN+="/usr/local/bin/superx-egpu-perf.sh remove", \
RUN+="/usr/bin/su nepharius -c '/home/nepharius/SuperX-goes-Arch/.venv/bin/python3 /home/nepharius/SuperX-goes-Arch/tools/steam-egpu-toggle.py igpu'"