{ pkgs, config, lib, ... }: with lib; let cfg = config.modules.desktop.gaming; in { options.modules.desktop = { gaming = { enable = mkEnableOption "desktop gaming support"; }; }; config = mkIf cfg.enable { # ========================================================================== # Gaming on Linux # # can give you an idea what works where and how. # Begineer Guide: # ========================================================================== # Steam runs in a nixpak sandbox (hardening/nixpaks/steam.nix) that the # home manager installs, so `programs.steam` is off: its `package` option # calls `.override` on whatever it is given, which a sandbox script cannot # answer, and it would install a second, unsandboxed steam plus `steam-run`. # What is kept from it: 32-bit graphics, which the game sandboxes mount as # /run/opengl-driver-32, and the controller/VR udev rules. Dropped with it: # `extest` (needs /dev/uinput, which no sandbox gets) and the host # protontricks (it would run Wine on game-writable prefixes; the umu # sandbox carries one instead). hardware.graphics.enable32Bit = true; hardware.steam-hardware.enable = true; # Expose DW-Proton to umu-run: umu ignores Steam's # STEAM_EXTRA_COMPAT_TOOLS_PATHS, and a store path baked into a generated # launcher goes stale on every dwproton-bin update. The nix-config-umu-game # installer reads this on the host; the umu sandbox # (hardening/nixpaks/umu.nix) sets the same path for the run/exec scripts. environment.variables.UMU_PROTONPATH = "${pkgs.dwproton-bin.steamcompattool}"; # GameScope: run a game in its own nested compositor and let it renice # itself for steadier frame pacing. GPU-specific PRIME render offload belongs # to the host, not here (see programs.gamescope.env in # hosts/idols-ai/hardware-nvidia.nix). # https://github.com/ValveSoftware/gamescope programs.gamescope = { enable = true; capSysNice = true; }; # The sysctls SteamOS ships in steamos-customizations-jupiter (file names # below), as carried by nix-gaming's platformOptimizations module. boot.kernel.sysctl = { # 20-shed.conf "kernel.sched_cfs_bandwidth_slice_us" = 3000; # 20-net-timeout.conf: lets a game that was killed and restarted quickly # rebind its TCP ports. "net.ipv4.tcp_fin_timeout" = 5; # 30-splitlock.conf: no deliberate slowdown when a game hits a split lock. "kernel.split_lock_mitigate" = 0; # 30-vm.conf: MAX_INT - MAPCOUNT_ELF_CORE_MARGIN, see include/linux/mm.h. "vm.max_map_count" = 2147483642; }; # Low-latency PipeWire: a 64/48000 minimum quantum plus realtime scheduling, # as in nix-gaming's pipewireLowLatency module. services.pipewire.extraConfig = { pipewire."99-lowlatency" = { "context.properties"."default.clock.min-quantum" = 64; "context.modules" = [ { name = "libpipewire-module-rt"; flags = [ "ifexists" "nofail" ]; args = { "nice.level" = -15; "rt.prio" = 88; "rt.time.soft" = 200000; "rt.time.hard" = 200000; }; } ]; }; pipewire-pulse."99-lowlatency"."pulse.properties" = { "server.address" = [ "unix:native" ]; "pulse.min.req" = "64/48000"; "pulse.min.quantum" = "64/48000"; "pulse.min.frag" = "64/48000"; }; client."99-lowlatency"."stream.properties" = { "node.latency" = "64/48000"; "resample.quality" = 1; }; }; # Optimise Linux system performance on demand # https://github.com/FeralInteractive/GameMode # https://wiki.archlinux.org/title/Gamemode # # Usage: # 1. For games/launchers which integrate GameMode support: # https://github.com/FeralInteractive/GameMode#apps-with-gamemode-integration # simply running the game will automatically activate GameMode. programs.gamemode.enable = true; }; }